All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/4] Qualcomm Shared Memory State Machines
@ 2015-08-27 17:37 ` Bjorn Andersson
  0 siblings, 0 replies; 21+ messages in thread
From: Bjorn Andersson @ 2015-08-27 17:37 UTC (permalink / raw)
  To: Alexandre Courbot, Ian Campbell, Linus Walleij, Mark Rutland,
	Pawel Moll, Rob Herring
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-msm-u79uwXL29TY76Z2rM5mHXA,
	linux-gpio-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA

This series implements the two different mechanisms for propagating single bit
state information, used on the various Qualcomm platforms.

The system was traditionally used by the modem and application processor to
convey information about boot progress, power states, error handling and so on.
This was implemented as SMSM, with status bits representing a single local
state.

As the complexity of the SoC grew the state bits array grew and the need for
targeting specific state information at specific remote processors appeared.
SMP2P solves this by having separate shared memory regions per processor-pair.

This state information is e.g. used to convey progress and status of remote
firmware loading. Individual bits maps to various stages of the boot and error
states.

As some of these states on some platforms are passed as physical signals
instead, the two drivers are modelled as gpio- and interrupt-controllers -
providing a nice abstraction both in device tree sense and Linux implementation
sense.

Bjorn Andersson (4):
  dt-binding: gpio: Add Qualcomm SMSM device tree documentation
  gpio: qcom-smsm: Add driver for Qualcomm SMSM
  dt-binding: gpio: Introduce qcom,smp2p binding documentation
  gpio: qcom-smp2p: Qualcomm Shared Memory Point to Point

 .../devicetree/bindings/gpio/qcom,smp2p.txt        | 112 ++++
 .../devicetree/bindings/gpio/qcom,smsm.txt         | 114 ++++
 drivers/gpio/Kconfig                               |  15 +
 drivers/gpio/Makefile                              |   2 +
 drivers/gpio/gpio-qcom-smp2p.c                     | 601 ++++++++++++++++++++
 drivers/gpio/gpio-qcom-smsm.c                      | 631 +++++++++++++++++++++
 6 files changed, 1475 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/gpio/qcom,smp2p.txt
 create mode 100644 Documentation/devicetree/bindings/gpio/qcom,smsm.txt
 create mode 100644 drivers/gpio/gpio-qcom-smp2p.c
 create mode 100644 drivers/gpio/gpio-qcom-smsm.c

-- 
1.8.2.2

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [PATCH 0/4] Qualcomm Shared Memory State Machines
@ 2015-08-27 17:37 ` Bjorn Andersson
  0 siblings, 0 replies; 21+ messages in thread
From: Bjorn Andersson @ 2015-08-27 17:37 UTC (permalink / raw)
  To: Alexandre Courbot, Ian Campbell, Linus Walleij, Mark Rutland,
	Pawel Moll, Rob Herring
  Cc: devicetree, linux-arm-msm, linux-gpio, linux-kernel

This series implements the two different mechanisms for propagating single bit
state information, used on the various Qualcomm platforms.

The system was traditionally used by the modem and application processor to
convey information about boot progress, power states, error handling and so on.
This was implemented as SMSM, with status bits representing a single local
state.

As the complexity of the SoC grew the state bits array grew and the need for
targeting specific state information at specific remote processors appeared.
SMP2P solves this by having separate shared memory regions per processor-pair.

This state information is e.g. used to convey progress and status of remote
firmware loading. Individual bits maps to various stages of the boot and error
states.

As some of these states on some platforms are passed as physical signals
instead, the two drivers are modelled as gpio- and interrupt-controllers -
providing a nice abstraction both in device tree sense and Linux implementation
sense.

Bjorn Andersson (4):
  dt-binding: gpio: Add Qualcomm SMSM device tree documentation
  gpio: qcom-smsm: Add driver for Qualcomm SMSM
  dt-binding: gpio: Introduce qcom,smp2p binding documentation
  gpio: qcom-smp2p: Qualcomm Shared Memory Point to Point

 .../devicetree/bindings/gpio/qcom,smp2p.txt        | 112 ++++
 .../devicetree/bindings/gpio/qcom,smsm.txt         | 114 ++++
 drivers/gpio/Kconfig                               |  15 +
 drivers/gpio/Makefile                              |   2 +
 drivers/gpio/gpio-qcom-smp2p.c                     | 601 ++++++++++++++++++++
 drivers/gpio/gpio-qcom-smsm.c                      | 631 +++++++++++++++++++++
 6 files changed, 1475 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/gpio/qcom,smp2p.txt
 create mode 100644 Documentation/devicetree/bindings/gpio/qcom,smsm.txt
 create mode 100644 drivers/gpio/gpio-qcom-smp2p.c
 create mode 100644 drivers/gpio/gpio-qcom-smsm.c

-- 
1.8.2.2


^ permalink raw reply	[flat|nested] 21+ messages in thread

* [PATCH 1/4] dt-binding: gpio: Add Qualcomm SMSM device tree documentation
  2015-08-27 17:37 ` Bjorn Andersson
@ 2015-08-27 17:37     ` Bjorn Andersson
  -1 siblings, 0 replies; 21+ messages in thread
From: Bjorn Andersson @ 2015-08-27 17:37 UTC (permalink / raw)
  To: Alexandre Courbot, Ian Campbell, Linus Walleij, Mark Rutland,
	Pawel Moll, Rob Herring
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-msm-u79uwXL29TY76Z2rM5mHXA,
	linux-gpio-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA

This documents a device tree binding for exposing the Qualcomm Shared
Memory State Machine as a set of gpio- and interrupt-controllers.

Signed-off-by: Bjorn Andersson <bjorn.andersson-/MT0OVThwyLZJqsBc5GL+g@public.gmane.org>
---
 .../devicetree/bindings/gpio/qcom,smsm.txt         | 114 +++++++++++++++++++++
 drivers/gpio/Kconfig                               |   8 ++
 drivers/gpio/Makefile                              |   1 +
 3 files changed, 123 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/gpio/qcom,smsm.txt

diff --git a/Documentation/devicetree/bindings/gpio/qcom,smsm.txt b/Documentation/devicetree/bindings/gpio/qcom,smsm.txt
new file mode 100644
index 000000000000..06201ba76594
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpio/qcom,smsm.txt
@@ -0,0 +1,114 @@
+Qualcomm Shared Memory State Machine
+
+The Shared Memory State Machine facilitates broadcasting of single bit state
+information between the processors in a Qualcomm SoC. Each processor is
+assigned 32 bits of state that can be modified. A processor can through a
+matrix of bitmaps signal subscription of notifications upon changes to a
+certain bit owned by a certain remote processor.
+
+This document defines the binding for a driver that implements and exposes this
+a GPIO controller and a set of interrupt controllers.
+
+- compatible:
+	Usage: required
+	Value type: <string>
+	Definition: must be one of:
+		    "qcom,smsm"
+
+- qcom,ipc-N:
+	Usage: required
+	Value type: <prop-encoded-array>
+	Definition: three entries specifying the outgoing ipc bit used for
+		    signaling the N:th remote processor
+		    - phandle to a syscon node representing the apcs registers
+		    - u32 representing offset to the register within the syscon
+		    - u32 representing the ipc bit within the register
+
+- qcom,local-host:
+	Usage: optional
+	Value type: <u32>
+	Definition: identifier of the local processor in the list of hosts, or
+		    in other words specifier of the column in the subscription
+		    matrix representing the local processor
+		    defaults to host 0
+
+- #address-cells:
+	Usage: required
+	Value type: <u32>
+	Definition: must be 1
+
+- #size-cells:
+	Usage: required
+	Value type: <u32>
+	Definition: must be 0
+
+= SUBNODES
+Each processor's state bits are described by a subnode of the smsm device node.
+A node can either be a gpio-controller - denoting the local processors bits -
+or an interrupt-controller - denoting a remote processors state bits.  The node
+names are not important.
+
+- reg:
+	Usage: required
+	Value type: <u32>
+	Definition: specifies the offset, in words, of the first bit for this
+		    entry
+
+- gpio-controller:
+	Usage: required for local entry
+	Value type: <empty>
+	Definition: marks the entry as a gpio-controller and the state bits to
+		    belong to the local processor
+
+- #gpio-cells:
+	Usage: required for local entry
+	Value type: <u32>
+	Definition: must be 2 - denotes bit number and GPIO flags
+
+- interrupt-controller:
+	Usage: required for remote entries
+	Value type: <empty>
+	Definition: marks the entry as a interrupt-controller and the state bits
+		    to belong to a remote processor
+
+- #interrupt-cells:
+	Usage: required for remote entries
+	Value type: <u32>
+	Definition: must be 2 - denotes bit number and IRQ flags
+
+- interrupts:
+	Usage: required for remote entries
+	Value type: <prop-encoded-array>
+	Definition: one entry specifying remote IRQ used by the remote processor
+		    to signal changes of its state bits
+
+
+= EXAMPLE
+The following example shows the SMEM setup for controlling properties of the
+wireless processor, defined from the 8974 apps processor's point-of-view. It
+encompasses one outbound entry and the outgoing interrupt for the wireless
+processor.
+
+smsm {
+	compatible = "qcom,smsm";
+
+	#address-cells = <1>;
+	#size-cells = <0>;
+
+	qcom,ipc-3 = <&apcs 8 19>;
+
+	apps_smsm: apps@0 {
+		reg = <0>;
+
+		gpio-controller;
+		#gpio-cells = <2>;
+	};
+
+	wcnss_smsm: wcnss@7 {
+		reg = <7>;
+		interrupts = <0 144 1>;
+
+		interrupt-controller;
+		#interrupt-cells = <2>;
+	};
+};
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index b4fc9e4d24c6..0e57b60faae8 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -354,6 +354,14 @@ config GPIO_PXA
 	help
 	  Say yes here to support the PXA GPIO device
 
+config GPIO_QCOM_SMSM
+	bool "Qualcomm Shared Memory State Machine"
+	depends on QCOM_SMEM
+	help
+	  Say yes here to support the Qualcomm Shared Memory State Machine.
+	  The state machine is represented by bits in shared memory and is
+	  exposed to the system as GPIOs.
+
 config GPIO_RCAR
 	tristate "Renesas R-Car GPIO"
 	depends on ARM && (ARCH_SHMOBILE || COMPILE_TEST)
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index f79a7c482a99..0fd213892403 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -75,6 +75,7 @@ obj-$(CONFIG_GPIO_PCF857X)	+= gpio-pcf857x.o
 obj-$(CONFIG_GPIO_PCH)		+= gpio-pch.o
 obj-$(CONFIG_GPIO_PL061)	+= gpio-pl061.o
 obj-$(CONFIG_GPIO_PXA)		+= gpio-pxa.o
+obj-$(CONFIG_GPIO_QCOM_SMSM)	+= gpio-qcom-smsm.o
 obj-$(CONFIG_GPIO_RC5T583)	+= gpio-rc5t583.o
 obj-$(CONFIG_GPIO_RDC321X)	+= gpio-rdc321x.o
 obj-$(CONFIG_GPIO_RCAR)		+= gpio-rcar.o
-- 
1.8.2.2

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 1/4] dt-binding: gpio: Add Qualcomm SMSM device tree documentation
@ 2015-08-27 17:37     ` Bjorn Andersson
  0 siblings, 0 replies; 21+ messages in thread
From: Bjorn Andersson @ 2015-08-27 17:37 UTC (permalink / raw)
  To: Alexandre Courbot, Ian Campbell, Linus Walleij, Mark Rutland,
	Pawel Moll, Rob Herring
  Cc: devicetree, linux-arm-msm, linux-gpio, linux-kernel

This documents a device tree binding for exposing the Qualcomm Shared
Memory State Machine as a set of gpio- and interrupt-controllers.

Signed-off-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
---
 .../devicetree/bindings/gpio/qcom,smsm.txt         | 114 +++++++++++++++++++++
 drivers/gpio/Kconfig                               |   8 ++
 drivers/gpio/Makefile                              |   1 +
 3 files changed, 123 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/gpio/qcom,smsm.txt

diff --git a/Documentation/devicetree/bindings/gpio/qcom,smsm.txt b/Documentation/devicetree/bindings/gpio/qcom,smsm.txt
new file mode 100644
index 000000000000..06201ba76594
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpio/qcom,smsm.txt
@@ -0,0 +1,114 @@
+Qualcomm Shared Memory State Machine
+
+The Shared Memory State Machine facilitates broadcasting of single bit state
+information between the processors in a Qualcomm SoC. Each processor is
+assigned 32 bits of state that can be modified. A processor can through a
+matrix of bitmaps signal subscription of notifications upon changes to a
+certain bit owned by a certain remote processor.
+
+This document defines the binding for a driver that implements and exposes this
+a GPIO controller and a set of interrupt controllers.
+
+- compatible:
+	Usage: required
+	Value type: <string>
+	Definition: must be one of:
+		    "qcom,smsm"
+
+- qcom,ipc-N:
+	Usage: required
+	Value type: <prop-encoded-array>
+	Definition: three entries specifying the outgoing ipc bit used for
+		    signaling the N:th remote processor
+		    - phandle to a syscon node representing the apcs registers
+		    - u32 representing offset to the register within the syscon
+		    - u32 representing the ipc bit within the register
+
+- qcom,local-host:
+	Usage: optional
+	Value type: <u32>
+	Definition: identifier of the local processor in the list of hosts, or
+		    in other words specifier of the column in the subscription
+		    matrix representing the local processor
+		    defaults to host 0
+
+- #address-cells:
+	Usage: required
+	Value type: <u32>
+	Definition: must be 1
+
+- #size-cells:
+	Usage: required
+	Value type: <u32>
+	Definition: must be 0
+
+= SUBNODES
+Each processor's state bits are described by a subnode of the smsm device node.
+A node can either be a gpio-controller - denoting the local processors bits -
+or an interrupt-controller - denoting a remote processors state bits.  The node
+names are not important.
+
+- reg:
+	Usage: required
+	Value type: <u32>
+	Definition: specifies the offset, in words, of the first bit for this
+		    entry
+
+- gpio-controller:
+	Usage: required for local entry
+	Value type: <empty>
+	Definition: marks the entry as a gpio-controller and the state bits to
+		    belong to the local processor
+
+- #gpio-cells:
+	Usage: required for local entry
+	Value type: <u32>
+	Definition: must be 2 - denotes bit number and GPIO flags
+
+- interrupt-controller:
+	Usage: required for remote entries
+	Value type: <empty>
+	Definition: marks the entry as a interrupt-controller and the state bits
+		    to belong to a remote processor
+
+- #interrupt-cells:
+	Usage: required for remote entries
+	Value type: <u32>
+	Definition: must be 2 - denotes bit number and IRQ flags
+
+- interrupts:
+	Usage: required for remote entries
+	Value type: <prop-encoded-array>
+	Definition: one entry specifying remote IRQ used by the remote processor
+		    to signal changes of its state bits
+
+
+= EXAMPLE
+The following example shows the SMEM setup for controlling properties of the
+wireless processor, defined from the 8974 apps processor's point-of-view. It
+encompasses one outbound entry and the outgoing interrupt for the wireless
+processor.
+
+smsm {
+	compatible = "qcom,smsm";
+
+	#address-cells = <1>;
+	#size-cells = <0>;
+
+	qcom,ipc-3 = <&apcs 8 19>;
+
+	apps_smsm: apps@0 {
+		reg = <0>;
+
+		gpio-controller;
+		#gpio-cells = <2>;
+	};
+
+	wcnss_smsm: wcnss@7 {
+		reg = <7>;
+		interrupts = <0 144 1>;
+
+		interrupt-controller;
+		#interrupt-cells = <2>;
+	};
+};
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index b4fc9e4d24c6..0e57b60faae8 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -354,6 +354,14 @@ config GPIO_PXA
 	help
 	  Say yes here to support the PXA GPIO device
 
+config GPIO_QCOM_SMSM
+	bool "Qualcomm Shared Memory State Machine"
+	depends on QCOM_SMEM
+	help
+	  Say yes here to support the Qualcomm Shared Memory State Machine.
+	  The state machine is represented by bits in shared memory and is
+	  exposed to the system as GPIOs.
+
 config GPIO_RCAR
 	tristate "Renesas R-Car GPIO"
 	depends on ARM && (ARCH_SHMOBILE || COMPILE_TEST)
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index f79a7c482a99..0fd213892403 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -75,6 +75,7 @@ obj-$(CONFIG_GPIO_PCF857X)	+= gpio-pcf857x.o
 obj-$(CONFIG_GPIO_PCH)		+= gpio-pch.o
 obj-$(CONFIG_GPIO_PL061)	+= gpio-pl061.o
 obj-$(CONFIG_GPIO_PXA)		+= gpio-pxa.o
+obj-$(CONFIG_GPIO_QCOM_SMSM)	+= gpio-qcom-smsm.o
 obj-$(CONFIG_GPIO_RC5T583)	+= gpio-rc5t583.o
 obj-$(CONFIG_GPIO_RDC321X)	+= gpio-rdc321x.o
 obj-$(CONFIG_GPIO_RCAR)		+= gpio-rcar.o
-- 
1.8.2.2


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 2/4] gpio: qcom-smsm: Add driver for Qualcomm SMSM
  2015-08-27 17:37 ` Bjorn Andersson
@ 2015-08-27 17:37   ` Bjorn Andersson
  -1 siblings, 0 replies; 21+ messages in thread
From: Bjorn Andersson @ 2015-08-27 17:37 UTC (permalink / raw)
  To: Alexandre Courbot, Ian Campbell, Linus Walleij
  Cc: Mark Rutland, Pawel Moll, Rob Herring, devicetree, linux-arm-msm,
	linux-gpio, linux-kernel

This driver exposed the Qualcomm Shared Memory State Machine bits as
GPIOs to the system.

Signed-off-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
---
 drivers/gpio/gpio-qcom-smsm.c | 631 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 631 insertions(+)
 create mode 100644 drivers/gpio/gpio-qcom-smsm.c

diff --git a/drivers/gpio/gpio-qcom-smsm.c b/drivers/gpio/gpio-qcom-smsm.c
new file mode 100644
index 000000000000..4b3f28c469b6
--- /dev/null
+++ b/drivers/gpio/gpio-qcom-smsm.c
@@ -0,0 +1,631 @@
+/*
+ * Copyright (c) 2015, Sony Mobile Communications Inc.
+ * Copyright (c) 2012-2013, 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/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/soc/qcom/smem.h>
+
+/*
+ * This driver implements the Qualcomm Shared Memory State Machine, a mechanism
+ * for communicating single bit state information to remote processors.
+ *
+ * The implementation is based on two sections of shared memory; the first
+ * holding the state bits and the second holding a matrix of subscription bits.
+ *
+ * The state bits are structured in entries of 32 bits, each belonging to one
+ * system in the SoC. The entry belonging to the local system is considered
+ * read-write, while the rest should be considered read-only.
+ *
+ * The subscription matrix consists of N bitmaps per entry, denoting interest
+ * in updates of the entry for each of the N hosts. Upon updating a state bit
+ * each host's subscription bitmap should be queried and the remote system
+ * should be interrupted if they request so.
+ *
+ * The subscription matrix is laid out in entry-major order:
+ * entry0: [host0 ... hostN]
+ *	.
+ *	.
+ * entryM: [host0 ... hostN]
+ *
+ * A third, optional, shared memory region might contain information regarding
+ * the number of entries in the state bitmap as well as number of columns in
+ * the subscription matrix.
+ */
+
+/*
+ * Shared memory identifiers, used to acquire handles to respective memory
+ * region.
+ */
+#define SMEM_SMSM_SHARED_STATE		85
+#define SMEM_SMSM_CPU_INTR_MASK		333
+#define SMEM_SMSM_SIZE_INFO		419
+
+/*
+ * Default sizes, in case SMEM_SMSM_SIZE_INFO is not found.
+ */
+#define SMSM_DEFAULT_NUM_ENTRIES	8
+#define SMSM_DEFAULT_NUM_HOSTS		3
+
+struct smsm_entry;
+struct smsm_host;
+
+/**
+ * struct qcom_smsm - smsm driver context
+ * @dev:	smsm device pointer
+ * @local_host:	column in the subscription matrix representing this system
+ * @num_hosts:	number of columns in the subscription matrix
+ * @num_entries: number of entries in the state map and rows in the subscription
+ *		matrix
+ * @local_state: pointer to the local processor's state bits
+ * @subscription: pointer to local processor's row in subscription matrix
+ * @chip:	gpio_chip for interfacing the state bits
+ * @entries:	context for each of the entries
+ * @hosts:	context for each of the hosts
+ */
+struct qcom_smsm {
+	struct device *dev;
+
+	u32 local_host;
+
+	u32 num_hosts;
+	u32 num_entries;
+
+	u32 *local_state;
+	u32 *subscription;
+	struct gpio_chip chip;
+
+	struct smsm_entry *entries;
+	struct smsm_host *hosts;
+};
+
+/**
+ * struct smsm_entry - per remote processor entry context
+ * @smsm:	back-reference to driver context
+ * @domain:	IRQ domain for this entry, if representing a remote system
+ * @irq_enabled: bitmap of which state bits IRQs are enabled
+ * @irq_rising:	bitmap tracking if rising bits should be propagated
+ * @irq_falling: bitmap tracking if falling bits should be propagated
+ * @last_value:	snapshot of state bits last time the interrupts where propagated
+ * @remote_state: pointer to this entry's state bits
+ * @subscription: pointer to a row in the subscription matrix representing this
+ *		entry
+ */
+struct smsm_entry {
+	struct qcom_smsm *smsm;
+
+	struct irq_domain *domain;
+	DECLARE_BITMAP(irq_enabled, 32);
+	DECLARE_BITMAP(irq_rising, 32);
+	DECLARE_BITMAP(irq_falling, 32);
+	u32 last_value;
+
+	u32 *remote_state;
+	u32 *subscription;
+};
+
+/**
+ * struct smsm_host - representation of a remote host
+ * @ipc_regmap:	regmap for outgoing interrupt
+ * @ipc_offset:	offset in @ipc_regmap for outgoing interrupt
+ * @ipc_bit:	bit in @ipc_regmap + @ipc_offset for outgoing interrupt
+ */
+struct smsm_host {
+	struct regmap *ipc_regmap;
+	int ipc_offset;
+	int ipc_bit;
+};
+
+/**
+ * smsm_gpio_output() - change bit in outgoing entry and inform subscribers
+ * @chip:	gpio_chip for the entry
+ * @offset:	bit in the entry
+ * @value:	new value
+ *
+ * Used to set and clear the bits in the outgoing/local entry and inform
+ * subscribers about the change.
+ */
+static int smsm_gpio_output(struct gpio_chip *chip, unsigned offset, int value)
+{
+	struct qcom_smsm *smsm = container_of(chip, struct qcom_smsm, chip);
+	struct smsm_host *hostp;
+	u32 host;
+	u32 orig;
+	u32 val;
+
+	/* Update the entry */
+	val = orig = readl(smsm->local_state);
+	if (value)
+		val |= BIT(offset);
+	else
+		val &= ~BIT(offset);
+
+	/* Don't signal if we didn't change the value */
+	if (val == orig)
+		goto done;
+
+	/* Write out the new value */
+	writel(val, smsm->local_state);
+
+	/* Make sure the value update is ordered before any kicks */
+	wmb();
+
+	/* Iterate over all hosts to check whom wants a kick */
+	for (host = 0; host < smsm->num_hosts; host++) {
+		hostp = &smsm->hosts[host];
+
+		val = readl(smsm->subscription + host);
+		if (val & BIT(offset) && hostp->ipc_regmap) {
+			regmap_write(hostp->ipc_regmap,
+				     hostp->ipc_offset,
+				     BIT(hostp->ipc_bit));
+		}
+	}
+
+done:
+	return 0;
+}
+
+static void smsm_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+	smsm_gpio_output(chip, offset, value);
+}
+
+static const struct gpio_chip smsm_gpio_chip = {
+	.direction_output = smsm_gpio_output,
+	.set = smsm_gpio_set,
+	.owner = THIS_MODULE,
+};
+
+/**
+ * smsm_intr() - cascading IRQ handler for SMSM
+ * @irq:	unused
+ * @data:	entry related to this IRQ
+ *
+ * This function cascades an incoming interrupt from a remote system, based on
+ * the state bits and configuration.
+ */
+static irqreturn_t smsm_intr(int irq, void *data)
+{
+	struct smsm_entry *entry = data;
+	unsigned i;
+	int irq_pin;
+	u32 changed;
+	u32 val;
+
+	val = readl(entry->remote_state);
+	changed = val ^ entry->last_value;
+	entry->last_value = val;
+
+	for_each_set_bit(i, entry->irq_enabled, 32) {
+		if (!(changed & BIT(i)))
+			continue;
+
+		if (val & BIT(i)) {
+			if (test_bit(i, entry->irq_rising)) {
+				irq_pin = irq_find_mapping(entry->domain, i);
+				handle_nested_irq(irq_pin);
+			}
+		} else {
+			if (test_bit(i, entry->irq_falling)) {
+				irq_pin = irq_find_mapping(entry->domain, i);
+				handle_nested_irq(irq_pin);
+			}
+		}
+	}
+
+	return IRQ_HANDLED;
+}
+
+/**
+ * smsm_mask_irq() - un-subscribe from cascades of IRQs of a certain staus bit
+ * @irqd:	IRQ handle to be masked
+ *
+ * This un-subscribes the local CPU from interrupts upon changes to the defines
+ * status bit. The bit is also cleared from cascading.
+ */
+static void smsm_mask_irq(struct irq_data *irqd)
+{
+	struct smsm_entry *entry = irq_data_get_irq_chip_data(irqd);
+	irq_hw_number_t irq = irqd_to_hwirq(irqd);
+	struct qcom_smsm *smsm = entry->smsm;
+	u32 val;
+
+	if (entry->subscription) {
+		val = readl(entry->subscription + smsm->local_host);
+		val &= ~BIT(irq);
+		writel(val, entry->subscription + smsm->local_host);
+	}
+
+	clear_bit(irq, entry->irq_enabled);
+}
+
+/**
+ * smsm_unmask_irq() - subscribe to cascades of IRQs of a certain status bit
+ * @irqd:	IRQ handle to be unmasked
+ *
+
+ * This subscribes the local CPU to interrupts upon changes to the defined
+ * status bit. The bit is also marked for cascading.
+
+ */
+static void smsm_unmask_irq(struct irq_data *irqd)
+{
+	struct smsm_entry *entry = irq_data_get_irq_chip_data(irqd);
+	irq_hw_number_t irq = irqd_to_hwirq(irqd);
+	struct qcom_smsm *smsm = entry->smsm;
+	u32 val;
+
+	set_bit(irq, entry->irq_enabled);
+
+	if (entry->subscription) {
+		val = readl(entry->subscription + smsm->local_host);
+		val |= BIT(irq);
+		writel(val, entry->subscription + smsm->local_host);
+	}
+}
+
+/**
+ * smsm_set_irq_type() - updates the requested IRQ type for the cascading
+ * @irqd:	consumer interrupt handle
+ * @type:	requested flags
+ */
+static int smsm_set_irq_type(struct irq_data *irqd, unsigned int type)
+{
+	struct smsm_entry *entry = irq_data_get_irq_chip_data(irqd);
+	irq_hw_number_t irq = irqd_to_hwirq(irqd);
+
+	if (!(type & IRQ_TYPE_EDGE_BOTH))
+		return -EINVAL;
+
+	if (type & IRQ_TYPE_EDGE_RISING)
+		set_bit(irq, entry->irq_rising);
+	else
+		clear_bit(irq, entry->irq_rising);
+
+	if (type & IRQ_TYPE_EDGE_FALLING)
+		set_bit(irq, entry->irq_falling);
+	else
+		clear_bit(irq, entry->irq_falling);
+
+	return 0;
+}
+
+static struct irq_chip smsm_irq_chip = {
+	.name           = "smsm",
+	.irq_mask       = smsm_mask_irq,
+	.irq_unmask     = smsm_unmask_irq,
+	.irq_set_type	= smsm_set_irq_type,
+};
+
+/**
+ * smsm_irq_map() - sets up a mapping for a cascaded IRQ
+ * @d:		IRQ domain representing an entry
+ * @irq:	IRQ to set up
+ * @hw:		unused
+ */
+static int smsm_irq_map(struct irq_domain *d,
+			unsigned int irq,
+			irq_hw_number_t hw)
+{
+	struct smsm_entry *entry = d->host_data;
+
+	irq_set_chip_and_handler(irq, &smsm_irq_chip, handle_level_irq);
+	irq_set_chip_data(irq, entry);
+	irq_set_nested_thread(irq, 1);
+
+	return 0;
+}
+
+static const struct irq_domain_ops smsm_irq_ops = {
+	.map = smsm_irq_map,
+	.xlate = irq_domain_xlate_twocell,
+};
+
+/**
+ * smsm_parse_ipc() - parses a qcom,ipc-%d device tree property
+ * @smsm:	smsm driver context
+ * @host_id:	index of the remote host to be resolved
+ *
+ * Parses device tree to acquire the information needed for sending the
+ * outgoing interrupts to a remote host - identified by @host_id.
+ */
+static int smsm_parse_ipc(struct qcom_smsm *smsm, unsigned host_id)
+{
+	struct device_node *syscon;
+	struct device_node *node = smsm->dev->of_node;
+	struct smsm_host *host = &smsm->hosts[host_id];
+	char key[16];
+	int ret;
+
+	snprintf(key, sizeof(key), "qcom,ipc-%d", host_id);
+	syscon = of_parse_phandle(node, key, 0);
+	if (!syscon)
+		return 0;
+
+	host->ipc_regmap = syscon_node_to_regmap(syscon);
+	if (IS_ERR(host->ipc_regmap))
+		return PTR_ERR(host->ipc_regmap);
+
+	ret = of_property_read_u32_index(node, key, 1, &host->ipc_offset);
+	if (ret < 0) {
+		dev_err(smsm->dev, "no offset in %s\n", key);
+		return -EINVAL;
+	}
+
+	ret = of_property_read_u32_index(node, key, 2, &host->ipc_bit);
+	if (ret < 0) {
+		dev_err(smsm->dev, "no bit in %s\n", key);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/**
+ * smsm_inbound_entry() - parse DT and set up an entry representing a remote system
+ * @smsm:	smsm driver context
+ * @entry:	entry context to be set up
+ * @node:	dt node containing the entry's properties
+ */
+static int smsm_inbound_entry(struct qcom_smsm *smsm,
+			      struct smsm_entry *entry,
+			      struct device_node *node)
+{
+	int ret;
+	int irq;
+
+	irq = irq_of_parse_and_map(node, 0);
+	if (!irq) {
+		dev_err(smsm->dev, "failed to parse smsm interrupt\n");
+		return -EINVAL;
+	}
+
+	ret = devm_request_threaded_irq(smsm->dev, irq,
+					NULL, smsm_intr,
+					IRQF_ONESHOT,
+					"smsm", (void *)entry);
+	if (ret) {
+		dev_err(smsm->dev, "failed to request interrupt\n");
+		return ret;
+	}
+
+	entry->domain = irq_domain_add_linear(node, 32, &smsm_irq_ops, entry);
+	if (!entry->domain) {
+		dev_err(smsm->dev, "failed to add irq_domain\n");
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+/**
+ * smsm_get_size_info() - parse the optional memory segment for sizes
+ * @smsm:	smsm driver context
+ *
+ * Attempt to acquire the number of hosts and entries from the optional shared
+ * memory location. Not being able to find this segment should indicate that
+ * we're on a older system where these values was hard coded to
+ * SMSM_DEFAULT_NUM_ENTRIES and SMSM_DEFAULT_NUM_HOSTS.
+ *
+ * Returns 0 on success, negative errno on failure.
+ */
+static int smsm_get_size_info(struct qcom_smsm *smsm)
+{
+	size_t size;
+	int ret;
+	struct {
+		u32 num_hosts;
+		u32 num_entries;
+		u32 reserved0;
+		u32 reserved1;
+	} *info;
+
+	ret = qcom_smem_get(QCOM_SMEM_HOST_ANY, SMEM_SMSM_SIZE_INFO,
+			    (void **)&info, &size);
+	if (ret == -ENOENT || size != sizeof(*info)) {
+		dev_warn(smsm->dev, "no smsm size info, using defaults\n");
+		smsm->num_entries = SMSM_DEFAULT_NUM_ENTRIES;
+		smsm->num_hosts = SMSM_DEFAULT_NUM_HOSTS;
+		return 0;
+	} else if (ret) {
+		dev_err(smsm->dev, "unable to retrieve smsm size info\n");
+		return ret;
+	}
+
+	smsm->num_entries = info->num_entries;
+	smsm->num_hosts = info->num_hosts;
+
+	dev_dbg(smsm->dev,
+		"found custom size of smsm: %d entries %d hosts\n",
+		smsm->num_entries, smsm->num_hosts);
+
+	return 0;
+}
+
+static int qcom_smsm_probe(struct platform_device *pdev)
+{
+	struct device_node *gpio_node;
+	struct device_node *node;
+	struct smsm_entry *entry;
+	struct qcom_smsm *smsm;
+	u32 *intr_mask;
+	size_t size;
+	u32 *states;
+	u32 id;
+	int ret;
+
+	smsm = devm_kzalloc(&pdev->dev, sizeof(*smsm), GFP_KERNEL);
+	if (!smsm)
+		return -ENOMEM;
+	smsm->dev = &pdev->dev;
+
+	ret = smsm_get_size_info(smsm);
+	if (ret)
+		return ret;
+
+	smsm->entries = devm_kcalloc(&pdev->dev,
+				     smsm->num_entries,
+				     sizeof(struct smsm_entry),
+				     GFP_KERNEL);
+	if (!smsm->entries)
+		return -ENOMEM;
+
+	smsm->hosts = devm_kcalloc(&pdev->dev,
+				   smsm->num_hosts,
+				   sizeof(struct smsm_host),
+				   GFP_KERNEL);
+	if (!smsm->hosts)
+		return -ENOMEM;
+
+	gpio_node = of_find_node_with_property(pdev->dev.of_node, "gpio-controller");
+	if (!gpio_node) {
+		dev_err(&pdev->dev, "no gpio-controller entry\n");
+		return -EINVAL;
+	}
+
+	of_property_read_u32(pdev->dev.of_node,
+			     "qcom,local-host",
+			     &smsm->local_host);
+
+	/* Parse the host properties */
+	for (id = 0; id < smsm->num_hosts; id++) {
+		ret = smsm_parse_ipc(smsm, id);
+		if (ret < 0)
+			return ret;
+	}
+
+	/* Acquire the main SMSM state vector */
+	ret = qcom_smem_alloc(QCOM_SMEM_HOST_ANY, SMEM_SMSM_SHARED_STATE,
+			      smsm->num_entries * sizeof(u32));
+	if (ret < 0 && ret != -EEXIST) {
+		dev_err(&pdev->dev, "unable to allocate shared state entry\n");
+		return ret;
+	}
+
+	ret = qcom_smem_get(QCOM_SMEM_HOST_ANY, SMEM_SMSM_SHARED_STATE,
+			    (void **)&states, NULL);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Unable to acquire shared state entry\n");
+		return ret;
+	}
+
+	/* Acquire the list of interrupt mask vectors */
+	size = smsm->num_entries * smsm->num_hosts * sizeof(u32);
+	ret = qcom_smem_alloc(QCOM_SMEM_HOST_ANY, SMEM_SMSM_CPU_INTR_MASK, size);
+	if (ret < 0 && ret != -EEXIST) {
+		dev_err(&pdev->dev, "unable to allocate smsm interrupt mask\n");
+		return ret;
+	}
+
+	ret = qcom_smem_get(QCOM_SMEM_HOST_ANY, SMEM_SMSM_CPU_INTR_MASK,
+			    (void **)&intr_mask, NULL);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "unable to acquire shared memory interrupt mask\n");
+		return ret;
+	}
+
+	/* Setup the reference to the local state bits */
+	smsm->local_state = states + smsm->local_host;
+	smsm->subscription = intr_mask + smsm->local_host * smsm->num_hosts;
+
+	/* Register the gpio_chip */
+	smsm->chip = smsm_gpio_chip;
+	smsm->chip.base = -1;
+	smsm->chip.dev = &pdev->dev;
+	smsm->chip.label = gpio_node->name;
+	smsm->chip.ngpio = 32;
+	smsm->chip.of_node = gpio_node;
+	ret = gpiochip_add(&smsm->chip);
+	if (ret) {
+		dev_err(smsm->dev, "failed register gpiochip\n");
+		return ret;
+	}
+
+	/* Register handlers for remote processor entries of interest. */
+	for_each_available_child_of_node(pdev->dev.of_node, node) {
+		if (!of_property_read_bool(node, "interrupt-controller"))
+			continue;
+
+		ret = of_property_read_u32(node, "reg", &id);
+		if (ret || id >= smsm->num_entries) {
+			dev_err(&pdev->dev, "invalid reg of entry\n");
+			if (!ret)
+				ret = -EINVAL;
+			goto unwind_interfaces;
+		}
+		entry = &smsm->entries[id];
+
+		entry->smsm = smsm;
+		entry->remote_state = states + id;
+
+		/* Setup subscription pointers and unsubscribe to any kicks */
+		entry->subscription = intr_mask + id * smsm->num_hosts;
+		writel(0, entry->subscription + smsm->local_host);
+
+		ret = smsm_inbound_entry(smsm, entry, node);
+		if (ret < 0)
+			goto unwind_interfaces;
+	}
+
+	platform_set_drvdata(pdev, smsm);
+
+	return 0;
+
+unwind_interfaces:
+	for (id = 0; id < smsm->num_entries; id++)
+		if (smsm->entries[id].domain)
+			irq_domain_remove(smsm->entries[id].domain);
+
+	gpiochip_remove(&smsm->chip);
+
+	return ret;
+}
+
+static int qcom_smsm_remove(struct platform_device *pdev)
+{
+	struct qcom_smsm *smsm = platform_get_drvdata(pdev);
+	unsigned id;
+
+	for (id = 0; id < smsm->num_entries; id++)
+		if (smsm->entries[id].domain)
+			irq_domain_remove(smsm->entries[id].domain);
+
+	gpiochip_remove(&smsm->chip);
+
+	return 0;
+}
+
+static const struct of_device_id qcom_smsm_of_match[] = {
+	{ .compatible = "qcom,smsm" },
+	{}
+};
+MODULE_DEVICE_TABLE(of, qcom_smsm_of_match);
+
+static struct platform_driver qcom_smsm_driver = {
+	.probe = qcom_smsm_probe,
+	.remove = qcom_smsm_remove,
+	.driver  = {
+		.name  = "qcom-smsm",
+		.of_match_table = qcom_smsm_of_match,
+	},
+};
+module_platform_driver(qcom_smsm_driver);
+
+MODULE_DESCRIPTION("Qualcomm Shared Memory State Machine driver");
+MODULE_LICENSE("GPL v2");
-- 
1.8.2.2

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 2/4] gpio: qcom-smsm: Add driver for Qualcomm SMSM
@ 2015-08-27 17:37   ` Bjorn Andersson
  0 siblings, 0 replies; 21+ messages in thread
From: Bjorn Andersson @ 2015-08-27 17:37 UTC (permalink / raw)
  To: Alexandre Courbot, Ian Campbell, Linus Walleij
  Cc: Mark Rutland, Pawel Moll, Rob Herring, devicetree, linux-arm-msm,
	linux-gpio, linux-kernel

This driver exposed the Qualcomm Shared Memory State Machine bits as
GPIOs to the system.

Signed-off-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
---
 drivers/gpio/gpio-qcom-smsm.c | 631 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 631 insertions(+)
 create mode 100644 drivers/gpio/gpio-qcom-smsm.c

diff --git a/drivers/gpio/gpio-qcom-smsm.c b/drivers/gpio/gpio-qcom-smsm.c
new file mode 100644
index 000000000000..4b3f28c469b6
--- /dev/null
+++ b/drivers/gpio/gpio-qcom-smsm.c
@@ -0,0 +1,631 @@
+/*
+ * Copyright (c) 2015, Sony Mobile Communications Inc.
+ * Copyright (c) 2012-2013, 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/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/soc/qcom/smem.h>
+
+/*
+ * This driver implements the Qualcomm Shared Memory State Machine, a mechanism
+ * for communicating single bit state information to remote processors.
+ *
+ * The implementation is based on two sections of shared memory; the first
+ * holding the state bits and the second holding a matrix of subscription bits.
+ *
+ * The state bits are structured in entries of 32 bits, each belonging to one
+ * system in the SoC. The entry belonging to the local system is considered
+ * read-write, while the rest should be considered read-only.
+ *
+ * The subscription matrix consists of N bitmaps per entry, denoting interest
+ * in updates of the entry for each of the N hosts. Upon updating a state bit
+ * each host's subscription bitmap should be queried and the remote system
+ * should be interrupted if they request so.
+ *
+ * The subscription matrix is laid out in entry-major order:
+ * entry0: [host0 ... hostN]
+ *	.
+ *	.
+ * entryM: [host0 ... hostN]
+ *
+ * A third, optional, shared memory region might contain information regarding
+ * the number of entries in the state bitmap as well as number of columns in
+ * the subscription matrix.
+ */
+
+/*
+ * Shared memory identifiers, used to acquire handles to respective memory
+ * region.
+ */
+#define SMEM_SMSM_SHARED_STATE		85
+#define SMEM_SMSM_CPU_INTR_MASK		333
+#define SMEM_SMSM_SIZE_INFO		419
+
+/*
+ * Default sizes, in case SMEM_SMSM_SIZE_INFO is not found.
+ */
+#define SMSM_DEFAULT_NUM_ENTRIES	8
+#define SMSM_DEFAULT_NUM_HOSTS		3
+
+struct smsm_entry;
+struct smsm_host;
+
+/**
+ * struct qcom_smsm - smsm driver context
+ * @dev:	smsm device pointer
+ * @local_host:	column in the subscription matrix representing this system
+ * @num_hosts:	number of columns in the subscription matrix
+ * @num_entries: number of entries in the state map and rows in the subscription
+ *		matrix
+ * @local_state: pointer to the local processor's state bits
+ * @subscription: pointer to local processor's row in subscription matrix
+ * @chip:	gpio_chip for interfacing the state bits
+ * @entries:	context for each of the entries
+ * @hosts:	context for each of the hosts
+ */
+struct qcom_smsm {
+	struct device *dev;
+
+	u32 local_host;
+
+	u32 num_hosts;
+	u32 num_entries;
+
+	u32 *local_state;
+	u32 *subscription;
+	struct gpio_chip chip;
+
+	struct smsm_entry *entries;
+	struct smsm_host *hosts;
+};
+
+/**
+ * struct smsm_entry - per remote processor entry context
+ * @smsm:	back-reference to driver context
+ * @domain:	IRQ domain for this entry, if representing a remote system
+ * @irq_enabled: bitmap of which state bits IRQs are enabled
+ * @irq_rising:	bitmap tracking if rising bits should be propagated
+ * @irq_falling: bitmap tracking if falling bits should be propagated
+ * @last_value:	snapshot of state bits last time the interrupts where propagated
+ * @remote_state: pointer to this entry's state bits
+ * @subscription: pointer to a row in the subscription matrix representing this
+ *		entry
+ */
+struct smsm_entry {
+	struct qcom_smsm *smsm;
+
+	struct irq_domain *domain;
+	DECLARE_BITMAP(irq_enabled, 32);
+	DECLARE_BITMAP(irq_rising, 32);
+	DECLARE_BITMAP(irq_falling, 32);
+	u32 last_value;
+
+	u32 *remote_state;
+	u32 *subscription;
+};
+
+/**
+ * struct smsm_host - representation of a remote host
+ * @ipc_regmap:	regmap for outgoing interrupt
+ * @ipc_offset:	offset in @ipc_regmap for outgoing interrupt
+ * @ipc_bit:	bit in @ipc_regmap + @ipc_offset for outgoing interrupt
+ */
+struct smsm_host {
+	struct regmap *ipc_regmap;
+	int ipc_offset;
+	int ipc_bit;
+};
+
+/**
+ * smsm_gpio_output() - change bit in outgoing entry and inform subscribers
+ * @chip:	gpio_chip for the entry
+ * @offset:	bit in the entry
+ * @value:	new value
+ *
+ * Used to set and clear the bits in the outgoing/local entry and inform
+ * subscribers about the change.
+ */
+static int smsm_gpio_output(struct gpio_chip *chip, unsigned offset, int value)
+{
+	struct qcom_smsm *smsm = container_of(chip, struct qcom_smsm, chip);
+	struct smsm_host *hostp;
+	u32 host;
+	u32 orig;
+	u32 val;
+
+	/* Update the entry */
+	val = orig = readl(smsm->local_state);
+	if (value)
+		val |= BIT(offset);
+	else
+		val &= ~BIT(offset);
+
+	/* Don't signal if we didn't change the value */
+	if (val == orig)
+		goto done;
+
+	/* Write out the new value */
+	writel(val, smsm->local_state);
+
+	/* Make sure the value update is ordered before any kicks */
+	wmb();
+
+	/* Iterate over all hosts to check whom wants a kick */
+	for (host = 0; host < smsm->num_hosts; host++) {
+		hostp = &smsm->hosts[host];
+
+		val = readl(smsm->subscription + host);
+		if (val & BIT(offset) && hostp->ipc_regmap) {
+			regmap_write(hostp->ipc_regmap,
+				     hostp->ipc_offset,
+				     BIT(hostp->ipc_bit));
+		}
+	}
+
+done:
+	return 0;
+}
+
+static void smsm_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+	smsm_gpio_output(chip, offset, value);
+}
+
+static const struct gpio_chip smsm_gpio_chip = {
+	.direction_output = smsm_gpio_output,
+	.set = smsm_gpio_set,
+	.owner = THIS_MODULE,
+};
+
+/**
+ * smsm_intr() - cascading IRQ handler for SMSM
+ * @irq:	unused
+ * @data:	entry related to this IRQ
+ *
+ * This function cascades an incoming interrupt from a remote system, based on
+ * the state bits and configuration.
+ */
+static irqreturn_t smsm_intr(int irq, void *data)
+{
+	struct smsm_entry *entry = data;
+	unsigned i;
+	int irq_pin;
+	u32 changed;
+	u32 val;
+
+	val = readl(entry->remote_state);
+	changed = val ^ entry->last_value;
+	entry->last_value = val;
+
+	for_each_set_bit(i, entry->irq_enabled, 32) {
+		if (!(changed & BIT(i)))
+			continue;
+
+		if (val & BIT(i)) {
+			if (test_bit(i, entry->irq_rising)) {
+				irq_pin = irq_find_mapping(entry->domain, i);
+				handle_nested_irq(irq_pin);
+			}
+		} else {
+			if (test_bit(i, entry->irq_falling)) {
+				irq_pin = irq_find_mapping(entry->domain, i);
+				handle_nested_irq(irq_pin);
+			}
+		}
+	}
+
+	return IRQ_HANDLED;
+}
+
+/**
+ * smsm_mask_irq() - un-subscribe from cascades of IRQs of a certain staus bit
+ * @irqd:	IRQ handle to be masked
+ *
+ * This un-subscribes the local CPU from interrupts upon changes to the defines
+ * status bit. The bit is also cleared from cascading.
+ */
+static void smsm_mask_irq(struct irq_data *irqd)
+{
+	struct smsm_entry *entry = irq_data_get_irq_chip_data(irqd);
+	irq_hw_number_t irq = irqd_to_hwirq(irqd);
+	struct qcom_smsm *smsm = entry->smsm;
+	u32 val;
+
+	if (entry->subscription) {
+		val = readl(entry->subscription + smsm->local_host);
+		val &= ~BIT(irq);
+		writel(val, entry->subscription + smsm->local_host);
+	}
+
+	clear_bit(irq, entry->irq_enabled);
+}
+
+/**
+ * smsm_unmask_irq() - subscribe to cascades of IRQs of a certain status bit
+ * @irqd:	IRQ handle to be unmasked
+ *
+
+ * This subscribes the local CPU to interrupts upon changes to the defined
+ * status bit. The bit is also marked for cascading.
+
+ */
+static void smsm_unmask_irq(struct irq_data *irqd)
+{
+	struct smsm_entry *entry = irq_data_get_irq_chip_data(irqd);
+	irq_hw_number_t irq = irqd_to_hwirq(irqd);
+	struct qcom_smsm *smsm = entry->smsm;
+	u32 val;
+
+	set_bit(irq, entry->irq_enabled);
+
+	if (entry->subscription) {
+		val = readl(entry->subscription + smsm->local_host);
+		val |= BIT(irq);
+		writel(val, entry->subscription + smsm->local_host);
+	}
+}
+
+/**
+ * smsm_set_irq_type() - updates the requested IRQ type for the cascading
+ * @irqd:	consumer interrupt handle
+ * @type:	requested flags
+ */
+static int smsm_set_irq_type(struct irq_data *irqd, unsigned int type)
+{
+	struct smsm_entry *entry = irq_data_get_irq_chip_data(irqd);
+	irq_hw_number_t irq = irqd_to_hwirq(irqd);
+
+	if (!(type & IRQ_TYPE_EDGE_BOTH))
+		return -EINVAL;
+
+	if (type & IRQ_TYPE_EDGE_RISING)
+		set_bit(irq, entry->irq_rising);
+	else
+		clear_bit(irq, entry->irq_rising);
+
+	if (type & IRQ_TYPE_EDGE_FALLING)
+		set_bit(irq, entry->irq_falling);
+	else
+		clear_bit(irq, entry->irq_falling);
+
+	return 0;
+}
+
+static struct irq_chip smsm_irq_chip = {
+	.name           = "smsm",
+	.irq_mask       = smsm_mask_irq,
+	.irq_unmask     = smsm_unmask_irq,
+	.irq_set_type	= smsm_set_irq_type,
+};
+
+/**
+ * smsm_irq_map() - sets up a mapping for a cascaded IRQ
+ * @d:		IRQ domain representing an entry
+ * @irq:	IRQ to set up
+ * @hw:		unused
+ */
+static int smsm_irq_map(struct irq_domain *d,
+			unsigned int irq,
+			irq_hw_number_t hw)
+{
+	struct smsm_entry *entry = d->host_data;
+
+	irq_set_chip_and_handler(irq, &smsm_irq_chip, handle_level_irq);
+	irq_set_chip_data(irq, entry);
+	irq_set_nested_thread(irq, 1);
+
+	return 0;
+}
+
+static const struct irq_domain_ops smsm_irq_ops = {
+	.map = smsm_irq_map,
+	.xlate = irq_domain_xlate_twocell,
+};
+
+/**
+ * smsm_parse_ipc() - parses a qcom,ipc-%d device tree property
+ * @smsm:	smsm driver context
+ * @host_id:	index of the remote host to be resolved
+ *
+ * Parses device tree to acquire the information needed for sending the
+ * outgoing interrupts to a remote host - identified by @host_id.
+ */
+static int smsm_parse_ipc(struct qcom_smsm *smsm, unsigned host_id)
+{
+	struct device_node *syscon;
+	struct device_node *node = smsm->dev->of_node;
+	struct smsm_host *host = &smsm->hosts[host_id];
+	char key[16];
+	int ret;
+
+	snprintf(key, sizeof(key), "qcom,ipc-%d", host_id);
+	syscon = of_parse_phandle(node, key, 0);
+	if (!syscon)
+		return 0;
+
+	host->ipc_regmap = syscon_node_to_regmap(syscon);
+	if (IS_ERR(host->ipc_regmap))
+		return PTR_ERR(host->ipc_regmap);
+
+	ret = of_property_read_u32_index(node, key, 1, &host->ipc_offset);
+	if (ret < 0) {
+		dev_err(smsm->dev, "no offset in %s\n", key);
+		return -EINVAL;
+	}
+
+	ret = of_property_read_u32_index(node, key, 2, &host->ipc_bit);
+	if (ret < 0) {
+		dev_err(smsm->dev, "no bit in %s\n", key);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/**
+ * smsm_inbound_entry() - parse DT and set up an entry representing a remote system
+ * @smsm:	smsm driver context
+ * @entry:	entry context to be set up
+ * @node:	dt node containing the entry's properties
+ */
+static int smsm_inbound_entry(struct qcom_smsm *smsm,
+			      struct smsm_entry *entry,
+			      struct device_node *node)
+{
+	int ret;
+	int irq;
+
+	irq = irq_of_parse_and_map(node, 0);
+	if (!irq) {
+		dev_err(smsm->dev, "failed to parse smsm interrupt\n");
+		return -EINVAL;
+	}
+
+	ret = devm_request_threaded_irq(smsm->dev, irq,
+					NULL, smsm_intr,
+					IRQF_ONESHOT,
+					"smsm", (void *)entry);
+	if (ret) {
+		dev_err(smsm->dev, "failed to request interrupt\n");
+		return ret;
+	}
+
+	entry->domain = irq_domain_add_linear(node, 32, &smsm_irq_ops, entry);
+	if (!entry->domain) {
+		dev_err(smsm->dev, "failed to add irq_domain\n");
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+/**
+ * smsm_get_size_info() - parse the optional memory segment for sizes
+ * @smsm:	smsm driver context
+ *
+ * Attempt to acquire the number of hosts and entries from the optional shared
+ * memory location. Not being able to find this segment should indicate that
+ * we're on a older system where these values was hard coded to
+ * SMSM_DEFAULT_NUM_ENTRIES and SMSM_DEFAULT_NUM_HOSTS.
+ *
+ * Returns 0 on success, negative errno on failure.
+ */
+static int smsm_get_size_info(struct qcom_smsm *smsm)
+{
+	size_t size;
+	int ret;
+	struct {
+		u32 num_hosts;
+		u32 num_entries;
+		u32 reserved0;
+		u32 reserved1;
+	} *info;
+
+	ret = qcom_smem_get(QCOM_SMEM_HOST_ANY, SMEM_SMSM_SIZE_INFO,
+			    (void **)&info, &size);
+	if (ret == -ENOENT || size != sizeof(*info)) {
+		dev_warn(smsm->dev, "no smsm size info, using defaults\n");
+		smsm->num_entries = SMSM_DEFAULT_NUM_ENTRIES;
+		smsm->num_hosts = SMSM_DEFAULT_NUM_HOSTS;
+		return 0;
+	} else if (ret) {
+		dev_err(smsm->dev, "unable to retrieve smsm size info\n");
+		return ret;
+	}
+
+	smsm->num_entries = info->num_entries;
+	smsm->num_hosts = info->num_hosts;
+
+	dev_dbg(smsm->dev,
+		"found custom size of smsm: %d entries %d hosts\n",
+		smsm->num_entries, smsm->num_hosts);
+
+	return 0;
+}
+
+static int qcom_smsm_probe(struct platform_device *pdev)
+{
+	struct device_node *gpio_node;
+	struct device_node *node;
+	struct smsm_entry *entry;
+	struct qcom_smsm *smsm;
+	u32 *intr_mask;
+	size_t size;
+	u32 *states;
+	u32 id;
+	int ret;
+
+	smsm = devm_kzalloc(&pdev->dev, sizeof(*smsm), GFP_KERNEL);
+	if (!smsm)
+		return -ENOMEM;
+	smsm->dev = &pdev->dev;
+
+	ret = smsm_get_size_info(smsm);
+	if (ret)
+		return ret;
+
+	smsm->entries = devm_kcalloc(&pdev->dev,
+				     smsm->num_entries,
+				     sizeof(struct smsm_entry),
+				     GFP_KERNEL);
+	if (!smsm->entries)
+		return -ENOMEM;
+
+	smsm->hosts = devm_kcalloc(&pdev->dev,
+				   smsm->num_hosts,
+				   sizeof(struct smsm_host),
+				   GFP_KERNEL);
+	if (!smsm->hosts)
+		return -ENOMEM;
+
+	gpio_node = of_find_node_with_property(pdev->dev.of_node, "gpio-controller");
+	if (!gpio_node) {
+		dev_err(&pdev->dev, "no gpio-controller entry\n");
+		return -EINVAL;
+	}
+
+	of_property_read_u32(pdev->dev.of_node,
+			     "qcom,local-host",
+			     &smsm->local_host);
+
+	/* Parse the host properties */
+	for (id = 0; id < smsm->num_hosts; id++) {
+		ret = smsm_parse_ipc(smsm, id);
+		if (ret < 0)
+			return ret;
+	}
+
+	/* Acquire the main SMSM state vector */
+	ret = qcom_smem_alloc(QCOM_SMEM_HOST_ANY, SMEM_SMSM_SHARED_STATE,
+			      smsm->num_entries * sizeof(u32));
+	if (ret < 0 && ret != -EEXIST) {
+		dev_err(&pdev->dev, "unable to allocate shared state entry\n");
+		return ret;
+	}
+
+	ret = qcom_smem_get(QCOM_SMEM_HOST_ANY, SMEM_SMSM_SHARED_STATE,
+			    (void **)&states, NULL);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Unable to acquire shared state entry\n");
+		return ret;
+	}
+
+	/* Acquire the list of interrupt mask vectors */
+	size = smsm->num_entries * smsm->num_hosts * sizeof(u32);
+	ret = qcom_smem_alloc(QCOM_SMEM_HOST_ANY, SMEM_SMSM_CPU_INTR_MASK, size);
+	if (ret < 0 && ret != -EEXIST) {
+		dev_err(&pdev->dev, "unable to allocate smsm interrupt mask\n");
+		return ret;
+	}
+
+	ret = qcom_smem_get(QCOM_SMEM_HOST_ANY, SMEM_SMSM_CPU_INTR_MASK,
+			    (void **)&intr_mask, NULL);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "unable to acquire shared memory interrupt mask\n");
+		return ret;
+	}
+
+	/* Setup the reference to the local state bits */
+	smsm->local_state = states + smsm->local_host;
+	smsm->subscription = intr_mask + smsm->local_host * smsm->num_hosts;
+
+	/* Register the gpio_chip */
+	smsm->chip = smsm_gpio_chip;
+	smsm->chip.base = -1;
+	smsm->chip.dev = &pdev->dev;
+	smsm->chip.label = gpio_node->name;
+	smsm->chip.ngpio = 32;
+	smsm->chip.of_node = gpio_node;
+	ret = gpiochip_add(&smsm->chip);
+	if (ret) {
+		dev_err(smsm->dev, "failed register gpiochip\n");
+		return ret;
+	}
+
+	/* Register handlers for remote processor entries of interest. */
+	for_each_available_child_of_node(pdev->dev.of_node, node) {
+		if (!of_property_read_bool(node, "interrupt-controller"))
+			continue;
+
+		ret = of_property_read_u32(node, "reg", &id);
+		if (ret || id >= smsm->num_entries) {
+			dev_err(&pdev->dev, "invalid reg of entry\n");
+			if (!ret)
+				ret = -EINVAL;
+			goto unwind_interfaces;
+		}
+		entry = &smsm->entries[id];
+
+		entry->smsm = smsm;
+		entry->remote_state = states + id;
+
+		/* Setup subscription pointers and unsubscribe to any kicks */
+		entry->subscription = intr_mask + id * smsm->num_hosts;
+		writel(0, entry->subscription + smsm->local_host);
+
+		ret = smsm_inbound_entry(smsm, entry, node);
+		if (ret < 0)
+			goto unwind_interfaces;
+	}
+
+	platform_set_drvdata(pdev, smsm);
+
+	return 0;
+
+unwind_interfaces:
+	for (id = 0; id < smsm->num_entries; id++)
+		if (smsm->entries[id].domain)
+			irq_domain_remove(smsm->entries[id].domain);
+
+	gpiochip_remove(&smsm->chip);
+
+	return ret;
+}
+
+static int qcom_smsm_remove(struct platform_device *pdev)
+{
+	struct qcom_smsm *smsm = platform_get_drvdata(pdev);
+	unsigned id;
+
+	for (id = 0; id < smsm->num_entries; id++)
+		if (smsm->entries[id].domain)
+			irq_domain_remove(smsm->entries[id].domain);
+
+	gpiochip_remove(&smsm->chip);
+
+	return 0;
+}
+
+static const struct of_device_id qcom_smsm_of_match[] = {
+	{ .compatible = "qcom,smsm" },
+	{}
+};
+MODULE_DEVICE_TABLE(of, qcom_smsm_of_match);
+
+static struct platform_driver qcom_smsm_driver = {
+	.probe = qcom_smsm_probe,
+	.remove = qcom_smsm_remove,
+	.driver  = {
+		.name  = "qcom-smsm",
+		.of_match_table = qcom_smsm_of_match,
+	},
+};
+module_platform_driver(qcom_smsm_driver);
+
+MODULE_DESCRIPTION("Qualcomm Shared Memory State Machine driver");
+MODULE_LICENSE("GPL v2");
-- 
1.8.2.2


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 3/4] dt-binding: gpio: Introduce qcom,smp2p binding documentation
  2015-08-27 17:37 ` Bjorn Andersson
@ 2015-08-27 17:37   ` Bjorn Andersson
  -1 siblings, 0 replies; 21+ messages in thread
From: Bjorn Andersson @ 2015-08-27 17:37 UTC (permalink / raw)
  To: Alexandre Courbot, Ian Campbell, Linus Walleij, Mark Rutland,
	Pawel Moll, Rob Herring
  Cc: devicetree, linux-arm-msm, linux-gpio, linux-kernel

Introduce binding documentation for the Qualcomm Shared Memory Point 2 Point
protocol implemented as gpio and interrupt controllers.

Signed-off-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
---
 .../devicetree/bindings/gpio/qcom,smp2p.txt        | 112 +++++++++++++++++++++
 1 file changed, 112 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/gpio/qcom,smp2p.txt

diff --git a/Documentation/devicetree/bindings/gpio/qcom,smp2p.txt b/Documentation/devicetree/bindings/gpio/qcom,smp2p.txt
new file mode 100644
index 000000000000..532c7d3ce3d2
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpio/qcom,smp2p.txt
@@ -0,0 +1,112 @@
+Qualcomm Shared Memory Point 2 Point binding
+
+The Shared Memory Point to Point (SMP2P) protocol facilitates communication of
+a single 32-bit value between two processors.  Each value has a single writer
+(the local side) and a single reader (the remote side).  Values are uniquely
+identified in the system by the directed edge (local processor ID to remote
+processor ID) and a string identifier. This documents defines the binding for a
+driver that implements and exposes this protocol as a set of GPIO and interrupt
+controllers.
+
+- compatible:
+	Usage: required
+	Value type: <string>
+	Definition: must be one of:
+		    "qcom,smp2p"
+
+- interrupts:
+	Usage: required
+	Value type: <prop-encoded-array>
+	Definition: one entry specifying the smp2p notification interrupt
+
+- qcom,ipc:
+	Usage: required
+	Value type: <prop-encoded-array>
+	Definition: three entries specifying the outgoing ipc bit used for
+		    signaling the remote end of the smp2p edge:
+		    - phandle to a syscon node representing the apcs registers
+		    - u32 representing offset to the register within the syscon
+		    - u32 representing the ipc bit within the register
+
+- qcom,smem:
+	Usage: required
+	Value type: <u32 array>
+	Definition: two identifiers of the inbound and outbound smem items used
+		    for this edge
+
+- qcom,local-pid:
+	Usage: required
+	Value type: <u32>
+	Definition: specifies the identfier of the local endpoint of this edge
+
+- qcom,remote-pid:
+	Usage: required
+	Value type: <u32>
+	Definition: specifies the identfier of the remote endpoint of this edge
+
+= SUBNODES
+Each SMP2P pair contain a set of inbound and outbound entries, these are
+described in subnodes of the smp2p device node. The node names are not
+important.
+
+- qcom,entry-name:
+	Usage: required
+	Value type: <string>
+	Definition: specifies the name of this entry, for inbound entries this
+		    will be used to match against the remotely allocated entry
+		    and for outbound entries this name is used for allocating
+		    entries
+
+- interrupt-controller:
+	Usage: required for incoming entries
+	Value type: <empty>
+	Definition: marks the entry as inbound; the node should be specified
+		    as a two cell interrupt-controller as defined in 
+		    "../interrupt-controller/interrupts.txt"
+
+- #interrupt-cells:
+	Usage: required for incoming entries
+	Value type: <u32>
+	Definition: must be 2 - denoting the bit in the entry and IRQ flags
+
+- gpio-controller:
+	Usage: required for outgoing entries
+	Value type: <empty>
+	Definition: marks the entry as outbound; the node should be specified
+		    as a two cell gpio-controller as defined in "gpio.txt"
+
+- #gpio-cells:
+	Usage: required for outgoing entries
+	Value type: <u32>
+	Definition: must be 2 - denoting the bit in the entry and GPIO flags
+
+= EXAMPLE
+The following example shows the SMP2P setup with the wireless processor,
+defined from the 8974 apps processor's point-of-view. It encompasses one
+inbound and one outbound entry:
+
+wcnss-smp2p {
+	compatible = "qcom,smp2p";
+	qcom,smem = <431>, <451>;
+
+	interrupts = <0 143 1>;
+
+	qcom,ipc = <&apcs 8 18>;
+
+	qcom,local-pid = <0>;
+	qcom,remote-pid = <4>;
+
+	wcnss_smp2p_out: master-kernel {
+		qcom,entry-name = "master-kernel";
+
+		gpio-controller;
+		#gpio-cells = <2>;
+	};
+
+	wcnss_smp2p_in: slave-kernel {
+		qcom,entry-name = "slave-kernel";
+
+		interrupt-controller;
+		#interrupt-cells = <2>;
+	};
+};
-- 
1.8.2.2

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 3/4] dt-binding: gpio: Introduce qcom,smp2p binding documentation
@ 2015-08-27 17:37   ` Bjorn Andersson
  0 siblings, 0 replies; 21+ messages in thread
From: Bjorn Andersson @ 2015-08-27 17:37 UTC (permalink / raw)
  To: Alexandre Courbot, Ian Campbell, Linus Walleij, Mark Rutland,
	Pawel Moll, Rob Herring
  Cc: devicetree, linux-arm-msm, linux-gpio, linux-kernel

Introduce binding documentation for the Qualcomm Shared Memory Point 2 Point
protocol implemented as gpio and interrupt controllers.

Signed-off-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
---
 .../devicetree/bindings/gpio/qcom,smp2p.txt        | 112 +++++++++++++++++++++
 1 file changed, 112 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/gpio/qcom,smp2p.txt

diff --git a/Documentation/devicetree/bindings/gpio/qcom,smp2p.txt b/Documentation/devicetree/bindings/gpio/qcom,smp2p.txt
new file mode 100644
index 000000000000..532c7d3ce3d2
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpio/qcom,smp2p.txt
@@ -0,0 +1,112 @@
+Qualcomm Shared Memory Point 2 Point binding
+
+The Shared Memory Point to Point (SMP2P) protocol facilitates communication of
+a single 32-bit value between two processors.  Each value has a single writer
+(the local side) and a single reader (the remote side).  Values are uniquely
+identified in the system by the directed edge (local processor ID to remote
+processor ID) and a string identifier. This documents defines the binding for a
+driver that implements and exposes this protocol as a set of GPIO and interrupt
+controllers.
+
+- compatible:
+	Usage: required
+	Value type: <string>
+	Definition: must be one of:
+		    "qcom,smp2p"
+
+- interrupts:
+	Usage: required
+	Value type: <prop-encoded-array>
+	Definition: one entry specifying the smp2p notification interrupt
+
+- qcom,ipc:
+	Usage: required
+	Value type: <prop-encoded-array>
+	Definition: three entries specifying the outgoing ipc bit used for
+		    signaling the remote end of the smp2p edge:
+		    - phandle to a syscon node representing the apcs registers
+		    - u32 representing offset to the register within the syscon
+		    - u32 representing the ipc bit within the register
+
+- qcom,smem:
+	Usage: required
+	Value type: <u32 array>
+	Definition: two identifiers of the inbound and outbound smem items used
+		    for this edge
+
+- qcom,local-pid:
+	Usage: required
+	Value type: <u32>
+	Definition: specifies the identfier of the local endpoint of this edge
+
+- qcom,remote-pid:
+	Usage: required
+	Value type: <u32>
+	Definition: specifies the identfier of the remote endpoint of this edge
+
+= SUBNODES
+Each SMP2P pair contain a set of inbound and outbound entries, these are
+described in subnodes of the smp2p device node. The node names are not
+important.
+
+- qcom,entry-name:
+	Usage: required
+	Value type: <string>
+	Definition: specifies the name of this entry, for inbound entries this
+		    will be used to match against the remotely allocated entry
+		    and for outbound entries this name is used for allocating
+		    entries
+
+- interrupt-controller:
+	Usage: required for incoming entries
+	Value type: <empty>
+	Definition: marks the entry as inbound; the node should be specified
+		    as a two cell interrupt-controller as defined in 
+		    "../interrupt-controller/interrupts.txt"
+
+- #interrupt-cells:
+	Usage: required for incoming entries
+	Value type: <u32>
+	Definition: must be 2 - denoting the bit in the entry and IRQ flags
+
+- gpio-controller:
+	Usage: required for outgoing entries
+	Value type: <empty>
+	Definition: marks the entry as outbound; the node should be specified
+		    as a two cell gpio-controller as defined in "gpio.txt"
+
+- #gpio-cells:
+	Usage: required for outgoing entries
+	Value type: <u32>
+	Definition: must be 2 - denoting the bit in the entry and GPIO flags
+
+= EXAMPLE
+The following example shows the SMP2P setup with the wireless processor,
+defined from the 8974 apps processor's point-of-view. It encompasses one
+inbound and one outbound entry:
+
+wcnss-smp2p {
+	compatible = "qcom,smp2p";
+	qcom,smem = <431>, <451>;
+
+	interrupts = <0 143 1>;
+
+	qcom,ipc = <&apcs 8 18>;
+
+	qcom,local-pid = <0>;
+	qcom,remote-pid = <4>;
+
+	wcnss_smp2p_out: master-kernel {
+		qcom,entry-name = "master-kernel";
+
+		gpio-controller;
+		#gpio-cells = <2>;
+	};
+
+	wcnss_smp2p_in: slave-kernel {
+		qcom,entry-name = "slave-kernel";
+
+		interrupt-controller;
+		#interrupt-cells = <2>;
+	};
+};
-- 
1.8.2.2


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 4/4] gpio: qcom-smp2p: Qualcomm Shared Memory Point to Point
  2015-08-27 17:37 ` Bjorn Andersson
@ 2015-08-27 17:37     ` Bjorn Andersson
  -1 siblings, 0 replies; 21+ messages in thread
From: Bjorn Andersson @ 2015-08-27 17:37 UTC (permalink / raw)
  To: Alexandre Courbot, Ian Campbell, Linus Walleij
  Cc: Mark Rutland, Pawel Moll, Rob Herring,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-msm-u79uwXL29TY76Z2rM5mHXA,
	linux-gpio-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA

Introduce the Qualcomm Shard Memory Point to Point driver, exposing bits
from the SMP2P protocol as gpio and interrupt controllers.

Signed-off-by: Bjorn Andersson <bjorn.andersson-/MT0OVThwyLZJqsBc5GL+g@public.gmane.org>
---
 drivers/gpio/Kconfig           |   7 +
 drivers/gpio/Makefile          |   1 +
 drivers/gpio/gpio-qcom-smp2p.c | 601 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 609 insertions(+)
 create mode 100644 drivers/gpio/gpio-qcom-smp2p.c

diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 0e57b60faae8..82c61ce5af8f 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -354,6 +354,13 @@ config GPIO_PXA
 	help
 	  Say yes here to support the PXA GPIO device
 
+config GPIO_QCOM_SMP2P
+	bool "Qualcomm Shared Memory Point to Point support"
+	depends on QCOM_SMEM
+	help
+	  Say yes here to support the Qualcomm Shared Memory Point to Point
+	  protocol, exposed to the system as GPIOs.
+
 config GPIO_QCOM_SMSM
 	bool "Qualcomm Shared Memory State Machine"
 	depends on QCOM_SMEM
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 0fd213892403..fdab869a7212 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -75,6 +75,7 @@ obj-$(CONFIG_GPIO_PCF857X)	+= gpio-pcf857x.o
 obj-$(CONFIG_GPIO_PCH)		+= gpio-pch.o
 obj-$(CONFIG_GPIO_PL061)	+= gpio-pl061.o
 obj-$(CONFIG_GPIO_PXA)		+= gpio-pxa.o
+obj-$(CONFIG_GPIO_QCOM_SMP2P)	+= gpio-qcom-smp2p.o
 obj-$(CONFIG_GPIO_QCOM_SMSM)	+= gpio-qcom-smsm.o
 obj-$(CONFIG_GPIO_RC5T583)	+= gpio-rc5t583.o
 obj-$(CONFIG_GPIO_RDC321X)	+= gpio-rdc321x.o
diff --git a/drivers/gpio/gpio-qcom-smp2p.c b/drivers/gpio/gpio-qcom-smp2p.c
new file mode 100644
index 000000000000..f2eb2f095cc0
--- /dev/null
+++ b/drivers/gpio/gpio-qcom-smp2p.c
@@ -0,0 +1,601 @@
+/*
+ * Copyright (c) 2015, Sony Mobile Communications AB.
+ * Copyright (c) 2012-2013, 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/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/soc/qcom/smem.h>
+#include <linux/spinlock.h>
+
+/*
+ * The Shared Memory Point to Point (SMP2P) protocol facilitates communication
+ * of a single 32-bit value between two processors.  Each value has a single
+ * writer (the local side) and a single reader (the remote side). Values are
+ * uniquely identified in the system by the directed edge (local processor ID
+ * to remote processor ID) and a string identifier.
+ *
+ * Each processor is responsible for creating the outgoing SMEM items and each
+ * item is writable by the local processor and readable by the remote
+ * processor.  By using two separate SMEM items that are single-reader and
+ * single-writer, SMP2P does not require any remote locking mechanisms.
+ *
+ * The driver uses the Linux GPIO and interrupt framework to expose a virtual
+ * GPIO for each outbound entry and a virtual interrupt controller for each
+ * inbound entry.
+ */
+
+#define SMP2P_MAX_ENTRY 16
+#define SMP2P_MAX_ENTRY_NAME 16
+
+#define SMP2P_FEATURE_SSR_ACK 0x1
+
+#define SMP2P_MAGIC 0x504d5324
+
+/**
+ * struct smp2p_smem_item - in memory communication structure
+ * @magic:		magic number
+ * @version:		version - must be 1
+ * @features:		features flag - currently unused
+ * @local_pid:		processor id of sending end
+ * @remote_pid:		processor id of receiving end
+ * @total_entries:	number of entries - always SMP2P_MAX_ENTRY
+ * @valid_entries:	number of allocated entries
+ * @flags:
+ * @entries:		individual communication entries
+ *     @name:		name of the entry
+ *     @value:		content of the entry
+ */
+struct smp2p_smem_item {
+	u32 magic;
+	u8 version;
+	unsigned features:24;
+	u16 local_pid;
+	u16 remote_pid;
+	u16 total_entries;
+	u16 valid_entries;
+	u32 flags;
+
+	struct {
+		u8 name[SMP2P_MAX_ENTRY_NAME];
+		u32 value;
+	} entries[SMP2P_MAX_ENTRY];
+} __packed;
+
+/**
+ * struct smp2p_entry - driver context matching one entry
+ * @node:	list entry to keep track of allocated entries
+ * @smp2p:	reference to the device driver context
+ * @name:	name of the entry, to match against smp2p_smem_item
+ * @value:	pointer to smp2p_smem_item entry value
+ * @last_value:	last handled value
+ * @domain:	irq_domain for inbound entries
+ * @irq_enabled:bitmap to track enabled irq bits
+ * @irq_rising:	bitmap to mark irq bits for rising detection
+ * @irq_falling:bitmap to mark irq bits for falling detection
+ * @chip:	gpio_chip for outbound entries
+ * @lock:	spinlock to protect read-modify-write of the value
+ */
+struct smp2p_entry {
+	struct list_head node;
+	struct qcom_smp2p *smp2p;
+
+	const char *name;
+	u32 *value;
+	u32 last_value;
+
+	struct irq_domain *domain;
+	DECLARE_BITMAP(irq_enabled, 32);
+	DECLARE_BITMAP(irq_rising, 32);
+	DECLARE_BITMAP(irq_falling, 32);
+
+	struct gpio_chip chip;
+
+	spinlock_t lock;
+};
+
+#define SMP2P_INBOUND	0
+#define SMP2P_OUTBOUND	1
+
+/**
+ * struct qcom_smp2p - device driver context
+ * @dev:	device driver handle
+ * @in:		pointer to the inbound smem item
+ * @smem_items:	ids of the two smem items
+ * @valid_entries: already scanned inbound entries
+ * @local_pid:	processor id of the inbound edge
+ * @remote_pid:	processor id of the outbound edge
+ * @ipc_regmap:	regmap for the outbound ipc
+ * @ipc_offset:	offset within the regmap
+ * @ipc_bit:	bit in regmap@offset to kick to signal remote processor
+ * @inbound:	list of inbound entries
+ * @outbound:	list of outbound entries
+ */
+struct qcom_smp2p {
+	struct device *dev;
+
+	struct smp2p_smem_item *in;
+	struct smp2p_smem_item *out;
+
+	unsigned smem_items[SMP2P_OUTBOUND + 1];
+
+	unsigned valid_entries;
+
+	unsigned local_pid;
+	unsigned remote_pid;
+
+	struct regmap *ipc_regmap;
+	int ipc_offset;
+	int ipc_bit;
+
+	struct list_head inbound;
+	struct list_head outbound;
+};
+
+static void qcom_smp2p_kick(struct qcom_smp2p *smp2p)
+{
+	/* Make sure any updated data is written before the kick */
+	wmb();
+	regmap_write(smp2p->ipc_regmap, smp2p->ipc_offset, BIT(smp2p->ipc_bit));
+}
+
+/**
+ * qcom_smp2p_intr() - interrupt handler for incoming notifications
+ * @irq:	unused
+ * @data:	smp2p driver context
+ *
+ * Handle notifications from the remote side to handle newly allocated entries
+ * or any changes to the state bits of existing entries.
+ */
+static irqreturn_t qcom_smp2p_intr(int irq, void *data)
+{
+	struct smp2p_smem_item *in;
+	struct smp2p_entry *entry;
+	struct qcom_smp2p *smp2p = data;
+	unsigned smem_id = smp2p->smem_items[SMP2P_INBOUND];
+	unsigned pid = smp2p->remote_pid;
+	size_t size;
+	int irq_pin;
+	u32 status;
+	char buf[SMP2P_MAX_ENTRY_NAME];
+	u32 val;
+	int ret;
+	int i;
+
+	in = smp2p->in;
+
+	/* Acquire smem item, if not already found */
+	if (!in) {
+		ret = qcom_smem_get(pid, smem_id, (void **)&in, &size);
+		if (ret < 0) {
+			dev_err(smp2p->dev,
+				"Unable to acquire remote smp2p item\n");
+			return IRQ_HANDLED;
+		}
+
+		smp2p->in = in;
+	}
+
+	/* Match newly created entries */
+	for (i = smp2p->valid_entries; i < in->valid_entries; i++) {
+		list_for_each_entry(entry, &smp2p->inbound, node) {
+			memcpy_fromio(buf, in->entries[i].name, sizeof(buf));
+			if (!strcmp(buf, entry->name)) {
+				entry->value = &in->entries[i].value;
+				break;
+			}
+		}
+	}
+	smp2p->valid_entries = i;
+
+	/* Fire interrupts based on any value changes */
+	list_for_each_entry(entry, &smp2p->inbound, node) {
+		/* Ignore entries not yet allocated by the remote side */
+		if (!entry->value)
+			continue;
+
+		val = readl(entry->value);
+
+		status = val ^ entry->last_value;
+		entry->last_value = val;
+
+		/* No changes of this entry? */
+		if (!status)
+			continue;
+
+		for_each_set_bit(i, entry->irq_enabled, 32) {
+			if (!(status & BIT(i)))
+				continue;
+
+			if (val & BIT(i)) {
+				if (test_bit(i, entry->irq_rising)) {
+					irq_pin = irq_find_mapping(entry->domain, i);
+					handle_nested_irq(irq_pin);
+				}
+			} else {
+				if (test_bit(i, entry->irq_falling)) {
+					irq_pin = irq_find_mapping(entry->domain, i);
+					handle_nested_irq(irq_pin);
+				}
+			}
+
+		}
+	}
+
+	return IRQ_HANDLED;
+}
+
+static void smp2p_mask_irq(struct irq_data *irqd)
+{
+	struct smp2p_entry *entry = irq_data_get_irq_chip_data(irqd);
+	irq_hw_number_t irq = irqd_to_hwirq(irqd);
+
+	clear_bit(irq, entry->irq_enabled);
+}
+
+static void smp2p_unmask_irq(struct irq_data *irqd)
+{
+	struct smp2p_entry *entry = irq_data_get_irq_chip_data(irqd);
+	irq_hw_number_t irq = irqd_to_hwirq(irqd);
+
+	set_bit(irq, entry->irq_enabled);
+}
+
+static int smp2p_set_irq_type(struct irq_data *irqd, unsigned int type)
+{
+	struct smp2p_entry *entry = irq_data_get_irq_chip_data(irqd);
+	irq_hw_number_t irq = irqd_to_hwirq(irqd);
+
+	if (!(type & IRQ_TYPE_EDGE_BOTH))
+		return -EINVAL;
+
+	if (type & IRQ_TYPE_EDGE_RISING)
+		set_bit(irq, entry->irq_rising);
+	else
+		clear_bit(irq, entry->irq_rising);
+
+	if (type & IRQ_TYPE_EDGE_FALLING)
+		set_bit(irq, entry->irq_falling);
+	else
+		clear_bit(irq, entry->irq_falling);
+
+	return 0;
+}
+
+static struct irq_chip smp2p_irq_chip = {
+	.name           = "smp2p",
+	.irq_mask       = smp2p_mask_irq,
+	.irq_unmask     = smp2p_unmask_irq,
+	.irq_set_type	= smp2p_set_irq_type,
+};
+
+static int smp2p_irq_map(struct irq_domain *d,
+			 unsigned int irq,
+			 irq_hw_number_t hw)
+{
+	struct smp2p_entry *entry = d->host_data;
+
+	irq_set_chip_and_handler(irq, &smp2p_irq_chip, handle_level_irq);
+	irq_set_chip_data(irq, entry);
+	irq_set_nested_thread(irq, 1);
+	irq_set_noprobe(irq);
+
+	return 0;
+}
+
+static const struct irq_domain_ops smp2p_irq_ops = {
+	.map = smp2p_irq_map,
+	.xlate = irq_domain_xlate_twocell,
+};
+
+static int smp2p_gpio_direction_output(struct gpio_chip *chip,
+				       unsigned offset,
+				       int value)
+{
+	struct smp2p_entry *entry = container_of(chip, struct smp2p_entry, chip);
+	u32 val;
+
+	spin_lock(&entry->lock);
+	val = readl(entry->value);
+	if (value)
+		val |= BIT(offset);
+	else
+		val &= ~BIT(offset);
+	writel(val, entry->value);
+	spin_unlock(&entry->lock);
+
+	qcom_smp2p_kick(entry->smp2p);
+
+	return 0;
+}
+
+static void smp2p_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+	smp2p_gpio_direction_output(chip, offset, value);
+}
+
+static const struct gpio_chip smp2p_gpio_chip = {
+	.set = smp2p_gpio_set,
+	.direction_output = smp2p_gpio_direction_output,
+	.owner = THIS_MODULE,
+};
+
+static int qcom_smp2p_inbound_entry(struct qcom_smp2p *smp2p,
+				    struct smp2p_entry *entry,
+				    struct device_node *node)
+{
+	entry->domain = irq_domain_add_linear(node, 32, &smp2p_irq_ops, entry);
+	if (!entry->domain) {
+		dev_err(smp2p->dev, "failed to add irq_domain\n");
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static int qcom_smp2p_outbound_entry(struct qcom_smp2p *smp2p,
+				     struct smp2p_entry *entry,
+				     struct device_node *node)
+{
+	struct smp2p_smem_item *out = smp2p->out;
+	char buf[SMP2P_MAX_ENTRY_NAME] = {};
+	int ret;
+
+	/* Allocate an entry from the smem item */
+	strlcpy(buf, entry->name, SMP2P_MAX_ENTRY_NAME);
+	memcpy_toio(out->entries[out->valid_entries].name, buf, SMP2P_MAX_ENTRY_NAME);
+	out->valid_entries++;
+
+	/* Make the logical entry reference the physical value */
+	entry->value = &out->entries[out->valid_entries].value;
+
+	entry->chip = smp2p_gpio_chip;
+	entry->chip.base = -1;
+	entry->chip.ngpio = 32;
+	entry->chip.label = entry->name;
+	entry->chip.dev = smp2p->dev;
+	entry->chip.of_node = node;
+
+	ret = gpiochip_add(&entry->chip);
+	if (ret)
+		dev_err(smp2p->dev, "failed register gpiochip\n");
+
+	return 0;
+}
+
+static int qcom_smp2p_alloc_outbound_item(struct qcom_smp2p *smp2p)
+{
+	struct smp2p_smem_item *out;
+	unsigned smem_id = smp2p->smem_items[SMP2P_OUTBOUND];
+	unsigned pid = smp2p->remote_pid;
+	int ret;
+
+	ret = qcom_smem_alloc(pid, smem_id, sizeof(*out));
+	if (ret < 0 && ret != -EEXIST) {
+		if (ret != -EPROBE_DEFER)
+			dev_err(smp2p->dev,
+				"unable to allocate local smp2p item\n");
+		return ret;
+	}
+
+	ret = qcom_smem_get(pid, smem_id, (void **)&out, NULL);
+	if (ret < 0) {
+		dev_err(smp2p->dev, "Unable to acquire local smp2p item\n");
+		return ret;
+	}
+
+	memset(out, 0, sizeof(*out));
+	out->magic = SMP2P_MAGIC;
+	out->local_pid = smp2p->local_pid;
+	out->remote_pid = smp2p->remote_pid;
+	out->total_entries = SMP2P_MAX_ENTRY;
+	out->valid_entries = 0;
+
+	/*
+	 * Make sure the rest of the header is written before we validate the
+	 * item by writing a valid version number.
+	 */
+	wmb();
+	out->version = 1;
+
+	qcom_smp2p_kick(smp2p);
+
+	smp2p->out = out;
+
+	return 0;
+}
+
+static int smp2p_parse_ipc(struct qcom_smp2p *smp2p)
+{
+	struct device_node *syscon;
+	struct device *dev = smp2p->dev;
+	const char *key;
+	int ret;
+
+	syscon = of_parse_phandle(dev->of_node, "qcom,ipc", 0);
+	if (!syscon) {
+		dev_err(dev, "no qcom,ipc node\n");
+		return -ENODEV;
+	}
+
+	smp2p->ipc_regmap = syscon_node_to_regmap(syscon);
+	if (IS_ERR(smp2p->ipc_regmap))
+		return PTR_ERR(smp2p->ipc_regmap);
+
+	key = "qcom,ipc";
+	ret = of_property_read_u32_index(dev->of_node, key, 1, &smp2p->ipc_offset);
+	if (ret < 0) {
+		dev_err(dev, "no offset in %s\n", key);
+		return -EINVAL;
+	}
+
+	ret = of_property_read_u32_index(dev->of_node, key, 2, &smp2p->ipc_bit);
+	if (ret < 0) {
+		dev_err(dev, "no bit in %s\n", key);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int qcom_smp2p_probe(struct platform_device *pdev)
+{
+	struct smp2p_entry *entry;
+	struct device_node *node;
+	struct qcom_smp2p *smp2p;
+	const char *key;
+	int irq;
+	int ret;
+
+	smp2p = devm_kzalloc(&pdev->dev, sizeof(*smp2p), GFP_KERNEL);
+	if (!smp2p)
+		return -ENOMEM;
+
+	smp2p->dev = &pdev->dev;
+	INIT_LIST_HEAD(&smp2p->inbound);
+	INIT_LIST_HEAD(&smp2p->outbound);
+
+	platform_set_drvdata(pdev, smp2p);
+
+	ret = smp2p_parse_ipc(smp2p);
+	if (ret)
+		return ret;
+
+	key = "qcom,smem";
+	ret = of_property_read_u32_array(pdev->dev.of_node, key,
+					 smp2p->smem_items, 2);
+	if (ret)
+		return ret;
+
+	key = "qcom,local-pid";
+	ret = of_property_read_u32(pdev->dev.of_node, key, &smp2p->local_pid);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed to read %s\n", key);
+		return -EINVAL;
+	}
+
+	key = "qcom,remote-pid";
+	ret = of_property_read_u32(pdev->dev.of_node, key, &smp2p->remote_pid);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed to read %s\n", key);
+		return -EINVAL;
+	}
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(&pdev->dev, "unable to acquire smp2p interrupt\n");
+		return irq;
+	}
+
+	ret = qcom_smp2p_alloc_outbound_item(smp2p);
+	if (ret < 0)
+		return ret;
+
+	for_each_available_child_of_node(pdev->dev.of_node, node) {
+		entry = devm_kzalloc(&pdev->dev, sizeof(*entry), GFP_KERNEL);
+		if (!entry) {
+			ret = -ENOMEM;
+			goto unwind_interfaces;
+		}
+
+		entry->smp2p = smp2p;
+		spin_lock_init(&entry->lock);
+
+		ret = of_property_read_string(node, "qcom,entry-name", &entry->name);
+		if (ret < 0)
+			goto unwind_interfaces;
+
+		if (of_property_read_bool(node, "interrupt-controller")) {
+			ret = qcom_smp2p_inbound_entry(smp2p, entry, node);
+			if (ret < 0)
+				goto unwind_interfaces;
+
+			list_add(&entry->node, &smp2p->inbound);
+		} else if (of_property_read_bool(node, "gpio-controller")) {
+			ret = qcom_smp2p_outbound_entry(smp2p, entry, node);
+			if (ret < 0)
+				goto unwind_interfaces;
+
+			list_add(&entry->node, &smp2p->outbound);
+		} else {
+			dev_err(&pdev->dev, "neither inbound nor outbound\n");
+			ret = -EINVAL;
+			goto unwind_interfaces;
+		}
+	}
+
+	/* Kick the outgoing edge after allocating entries */
+	qcom_smp2p_kick(smp2p);
+
+	ret = devm_request_threaded_irq(&pdev->dev, irq,
+					NULL, qcom_smp2p_intr,
+					IRQF_ONESHOT,
+					"smp2p", (void *)smp2p);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to request interrupt\n");
+		goto unwind_interfaces;
+	}
+
+
+	return 0;
+
+unwind_interfaces:
+	list_for_each_entry(entry, &smp2p->inbound, node)
+		irq_domain_remove(entry->domain);
+
+	list_for_each_entry(entry, &smp2p->outbound, node)
+		gpiochip_remove(&entry->chip);
+
+	smp2p->out->valid_entries = 0;
+
+	return ret;
+}
+
+static int qcom_smp2p_remove(struct platform_device *pdev)
+{
+	struct qcom_smp2p *smp2p = platform_get_drvdata(pdev);
+	struct smp2p_entry *entry;
+
+	list_for_each_entry(entry, &smp2p->inbound, node)
+		irq_domain_remove(entry->domain);
+
+	list_for_each_entry(entry, &smp2p->outbound, node)
+		gpiochip_remove(&entry->chip);
+
+	smp2p->out->valid_entries = 0;
+
+	return 0;
+}
+
+static const struct of_device_id qcom_smp2p_of_match[] = {
+	{ .compatible = "qcom,smp2p" },
+	{}
+};
+MODULE_DEVICE_TABLE(of, qcom_smp2p_of_match);
+
+static struct platform_driver qcom_smp2p_driver = {
+	.probe = qcom_smp2p_probe,
+	.remove = qcom_smp2p_remove,
+	.driver  = {
+		.name  = "qcom_smp2p",
+		.of_match_table = qcom_smp2p_of_match,
+	},
+};
+module_platform_driver(qcom_smp2p_driver);
+
+MODULE_DESCRIPTION("Qualcomm Shared Memory Point to Point driver");
+MODULE_LICENSE("GPL v2");
-- 
1.8.2.2

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 4/4] gpio: qcom-smp2p: Qualcomm Shared Memory Point to Point
@ 2015-08-27 17:37     ` Bjorn Andersson
  0 siblings, 0 replies; 21+ messages in thread
From: Bjorn Andersson @ 2015-08-27 17:37 UTC (permalink / raw)
  To: Alexandre Courbot, Ian Campbell, Linus Walleij
  Cc: Mark Rutland, Pawel Moll, Rob Herring, devicetree, linux-arm-msm,
	linux-gpio, linux-kernel

Introduce the Qualcomm Shard Memory Point to Point driver, exposing bits
from the SMP2P protocol as gpio and interrupt controllers.

Signed-off-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
---
 drivers/gpio/Kconfig           |   7 +
 drivers/gpio/Makefile          |   1 +
 drivers/gpio/gpio-qcom-smp2p.c | 601 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 609 insertions(+)
 create mode 100644 drivers/gpio/gpio-qcom-smp2p.c

diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 0e57b60faae8..82c61ce5af8f 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -354,6 +354,13 @@ config GPIO_PXA
 	help
 	  Say yes here to support the PXA GPIO device
 
+config GPIO_QCOM_SMP2P
+	bool "Qualcomm Shared Memory Point to Point support"
+	depends on QCOM_SMEM
+	help
+	  Say yes here to support the Qualcomm Shared Memory Point to Point
+	  protocol, exposed to the system as GPIOs.
+
 config GPIO_QCOM_SMSM
 	bool "Qualcomm Shared Memory State Machine"
 	depends on QCOM_SMEM
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 0fd213892403..fdab869a7212 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -75,6 +75,7 @@ obj-$(CONFIG_GPIO_PCF857X)	+= gpio-pcf857x.o
 obj-$(CONFIG_GPIO_PCH)		+= gpio-pch.o
 obj-$(CONFIG_GPIO_PL061)	+= gpio-pl061.o
 obj-$(CONFIG_GPIO_PXA)		+= gpio-pxa.o
+obj-$(CONFIG_GPIO_QCOM_SMP2P)	+= gpio-qcom-smp2p.o
 obj-$(CONFIG_GPIO_QCOM_SMSM)	+= gpio-qcom-smsm.o
 obj-$(CONFIG_GPIO_RC5T583)	+= gpio-rc5t583.o
 obj-$(CONFIG_GPIO_RDC321X)	+= gpio-rdc321x.o
diff --git a/drivers/gpio/gpio-qcom-smp2p.c b/drivers/gpio/gpio-qcom-smp2p.c
new file mode 100644
index 000000000000..f2eb2f095cc0
--- /dev/null
+++ b/drivers/gpio/gpio-qcom-smp2p.c
@@ -0,0 +1,601 @@
+/*
+ * Copyright (c) 2015, Sony Mobile Communications AB.
+ * Copyright (c) 2012-2013, 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/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/soc/qcom/smem.h>
+#include <linux/spinlock.h>
+
+/*
+ * The Shared Memory Point to Point (SMP2P) protocol facilitates communication
+ * of a single 32-bit value between two processors.  Each value has a single
+ * writer (the local side) and a single reader (the remote side). Values are
+ * uniquely identified in the system by the directed edge (local processor ID
+ * to remote processor ID) and a string identifier.
+ *
+ * Each processor is responsible for creating the outgoing SMEM items and each
+ * item is writable by the local processor and readable by the remote
+ * processor.  By using two separate SMEM items that are single-reader and
+ * single-writer, SMP2P does not require any remote locking mechanisms.
+ *
+ * The driver uses the Linux GPIO and interrupt framework to expose a virtual
+ * GPIO for each outbound entry and a virtual interrupt controller for each
+ * inbound entry.
+ */
+
+#define SMP2P_MAX_ENTRY 16
+#define SMP2P_MAX_ENTRY_NAME 16
+
+#define SMP2P_FEATURE_SSR_ACK 0x1
+
+#define SMP2P_MAGIC 0x504d5324
+
+/**
+ * struct smp2p_smem_item - in memory communication structure
+ * @magic:		magic number
+ * @version:		version - must be 1
+ * @features:		features flag - currently unused
+ * @local_pid:		processor id of sending end
+ * @remote_pid:		processor id of receiving end
+ * @total_entries:	number of entries - always SMP2P_MAX_ENTRY
+ * @valid_entries:	number of allocated entries
+ * @flags:
+ * @entries:		individual communication entries
+ *     @name:		name of the entry
+ *     @value:		content of the entry
+ */
+struct smp2p_smem_item {
+	u32 magic;
+	u8 version;
+	unsigned features:24;
+	u16 local_pid;
+	u16 remote_pid;
+	u16 total_entries;
+	u16 valid_entries;
+	u32 flags;
+
+	struct {
+		u8 name[SMP2P_MAX_ENTRY_NAME];
+		u32 value;
+	} entries[SMP2P_MAX_ENTRY];
+} __packed;
+
+/**
+ * struct smp2p_entry - driver context matching one entry
+ * @node:	list entry to keep track of allocated entries
+ * @smp2p:	reference to the device driver context
+ * @name:	name of the entry, to match against smp2p_smem_item
+ * @value:	pointer to smp2p_smem_item entry value
+ * @last_value:	last handled value
+ * @domain:	irq_domain for inbound entries
+ * @irq_enabled:bitmap to track enabled irq bits
+ * @irq_rising:	bitmap to mark irq bits for rising detection
+ * @irq_falling:bitmap to mark irq bits for falling detection
+ * @chip:	gpio_chip for outbound entries
+ * @lock:	spinlock to protect read-modify-write of the value
+ */
+struct smp2p_entry {
+	struct list_head node;
+	struct qcom_smp2p *smp2p;
+
+	const char *name;
+	u32 *value;
+	u32 last_value;
+
+	struct irq_domain *domain;
+	DECLARE_BITMAP(irq_enabled, 32);
+	DECLARE_BITMAP(irq_rising, 32);
+	DECLARE_BITMAP(irq_falling, 32);
+
+	struct gpio_chip chip;
+
+	spinlock_t lock;
+};
+
+#define SMP2P_INBOUND	0
+#define SMP2P_OUTBOUND	1
+
+/**
+ * struct qcom_smp2p - device driver context
+ * @dev:	device driver handle
+ * @in:		pointer to the inbound smem item
+ * @smem_items:	ids of the two smem items
+ * @valid_entries: already scanned inbound entries
+ * @local_pid:	processor id of the inbound edge
+ * @remote_pid:	processor id of the outbound edge
+ * @ipc_regmap:	regmap for the outbound ipc
+ * @ipc_offset:	offset within the regmap
+ * @ipc_bit:	bit in regmap@offset to kick to signal remote processor
+ * @inbound:	list of inbound entries
+ * @outbound:	list of outbound entries
+ */
+struct qcom_smp2p {
+	struct device *dev;
+
+	struct smp2p_smem_item *in;
+	struct smp2p_smem_item *out;
+
+	unsigned smem_items[SMP2P_OUTBOUND + 1];
+
+	unsigned valid_entries;
+
+	unsigned local_pid;
+	unsigned remote_pid;
+
+	struct regmap *ipc_regmap;
+	int ipc_offset;
+	int ipc_bit;
+
+	struct list_head inbound;
+	struct list_head outbound;
+};
+
+static void qcom_smp2p_kick(struct qcom_smp2p *smp2p)
+{
+	/* Make sure any updated data is written before the kick */
+	wmb();
+	regmap_write(smp2p->ipc_regmap, smp2p->ipc_offset, BIT(smp2p->ipc_bit));
+}
+
+/**
+ * qcom_smp2p_intr() - interrupt handler for incoming notifications
+ * @irq:	unused
+ * @data:	smp2p driver context
+ *
+ * Handle notifications from the remote side to handle newly allocated entries
+ * or any changes to the state bits of existing entries.
+ */
+static irqreturn_t qcom_smp2p_intr(int irq, void *data)
+{
+	struct smp2p_smem_item *in;
+	struct smp2p_entry *entry;
+	struct qcom_smp2p *smp2p = data;
+	unsigned smem_id = smp2p->smem_items[SMP2P_INBOUND];
+	unsigned pid = smp2p->remote_pid;
+	size_t size;
+	int irq_pin;
+	u32 status;
+	char buf[SMP2P_MAX_ENTRY_NAME];
+	u32 val;
+	int ret;
+	int i;
+
+	in = smp2p->in;
+
+	/* Acquire smem item, if not already found */
+	if (!in) {
+		ret = qcom_smem_get(pid, smem_id, (void **)&in, &size);
+		if (ret < 0) {
+			dev_err(smp2p->dev,
+				"Unable to acquire remote smp2p item\n");
+			return IRQ_HANDLED;
+		}
+
+		smp2p->in = in;
+	}
+
+	/* Match newly created entries */
+	for (i = smp2p->valid_entries; i < in->valid_entries; i++) {
+		list_for_each_entry(entry, &smp2p->inbound, node) {
+			memcpy_fromio(buf, in->entries[i].name, sizeof(buf));
+			if (!strcmp(buf, entry->name)) {
+				entry->value = &in->entries[i].value;
+				break;
+			}
+		}
+	}
+	smp2p->valid_entries = i;
+
+	/* Fire interrupts based on any value changes */
+	list_for_each_entry(entry, &smp2p->inbound, node) {
+		/* Ignore entries not yet allocated by the remote side */
+		if (!entry->value)
+			continue;
+
+		val = readl(entry->value);
+
+		status = val ^ entry->last_value;
+		entry->last_value = val;
+
+		/* No changes of this entry? */
+		if (!status)
+			continue;
+
+		for_each_set_bit(i, entry->irq_enabled, 32) {
+			if (!(status & BIT(i)))
+				continue;
+
+			if (val & BIT(i)) {
+				if (test_bit(i, entry->irq_rising)) {
+					irq_pin = irq_find_mapping(entry->domain, i);
+					handle_nested_irq(irq_pin);
+				}
+			} else {
+				if (test_bit(i, entry->irq_falling)) {
+					irq_pin = irq_find_mapping(entry->domain, i);
+					handle_nested_irq(irq_pin);
+				}
+			}
+
+		}
+	}
+
+	return IRQ_HANDLED;
+}
+
+static void smp2p_mask_irq(struct irq_data *irqd)
+{
+	struct smp2p_entry *entry = irq_data_get_irq_chip_data(irqd);
+	irq_hw_number_t irq = irqd_to_hwirq(irqd);
+
+	clear_bit(irq, entry->irq_enabled);
+}
+
+static void smp2p_unmask_irq(struct irq_data *irqd)
+{
+	struct smp2p_entry *entry = irq_data_get_irq_chip_data(irqd);
+	irq_hw_number_t irq = irqd_to_hwirq(irqd);
+
+	set_bit(irq, entry->irq_enabled);
+}
+
+static int smp2p_set_irq_type(struct irq_data *irqd, unsigned int type)
+{
+	struct smp2p_entry *entry = irq_data_get_irq_chip_data(irqd);
+	irq_hw_number_t irq = irqd_to_hwirq(irqd);
+
+	if (!(type & IRQ_TYPE_EDGE_BOTH))
+		return -EINVAL;
+
+	if (type & IRQ_TYPE_EDGE_RISING)
+		set_bit(irq, entry->irq_rising);
+	else
+		clear_bit(irq, entry->irq_rising);
+
+	if (type & IRQ_TYPE_EDGE_FALLING)
+		set_bit(irq, entry->irq_falling);
+	else
+		clear_bit(irq, entry->irq_falling);
+
+	return 0;
+}
+
+static struct irq_chip smp2p_irq_chip = {
+	.name           = "smp2p",
+	.irq_mask       = smp2p_mask_irq,
+	.irq_unmask     = smp2p_unmask_irq,
+	.irq_set_type	= smp2p_set_irq_type,
+};
+
+static int smp2p_irq_map(struct irq_domain *d,
+			 unsigned int irq,
+			 irq_hw_number_t hw)
+{
+	struct smp2p_entry *entry = d->host_data;
+
+	irq_set_chip_and_handler(irq, &smp2p_irq_chip, handle_level_irq);
+	irq_set_chip_data(irq, entry);
+	irq_set_nested_thread(irq, 1);
+	irq_set_noprobe(irq);
+
+	return 0;
+}
+
+static const struct irq_domain_ops smp2p_irq_ops = {
+	.map = smp2p_irq_map,
+	.xlate = irq_domain_xlate_twocell,
+};
+
+static int smp2p_gpio_direction_output(struct gpio_chip *chip,
+				       unsigned offset,
+				       int value)
+{
+	struct smp2p_entry *entry = container_of(chip, struct smp2p_entry, chip);
+	u32 val;
+
+	spin_lock(&entry->lock);
+	val = readl(entry->value);
+	if (value)
+		val |= BIT(offset);
+	else
+		val &= ~BIT(offset);
+	writel(val, entry->value);
+	spin_unlock(&entry->lock);
+
+	qcom_smp2p_kick(entry->smp2p);
+
+	return 0;
+}
+
+static void smp2p_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+	smp2p_gpio_direction_output(chip, offset, value);
+}
+
+static const struct gpio_chip smp2p_gpio_chip = {
+	.set = smp2p_gpio_set,
+	.direction_output = smp2p_gpio_direction_output,
+	.owner = THIS_MODULE,
+};
+
+static int qcom_smp2p_inbound_entry(struct qcom_smp2p *smp2p,
+				    struct smp2p_entry *entry,
+				    struct device_node *node)
+{
+	entry->domain = irq_domain_add_linear(node, 32, &smp2p_irq_ops, entry);
+	if (!entry->domain) {
+		dev_err(smp2p->dev, "failed to add irq_domain\n");
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static int qcom_smp2p_outbound_entry(struct qcom_smp2p *smp2p,
+				     struct smp2p_entry *entry,
+				     struct device_node *node)
+{
+	struct smp2p_smem_item *out = smp2p->out;
+	char buf[SMP2P_MAX_ENTRY_NAME] = {};
+	int ret;
+
+	/* Allocate an entry from the smem item */
+	strlcpy(buf, entry->name, SMP2P_MAX_ENTRY_NAME);
+	memcpy_toio(out->entries[out->valid_entries].name, buf, SMP2P_MAX_ENTRY_NAME);
+	out->valid_entries++;
+
+	/* Make the logical entry reference the physical value */
+	entry->value = &out->entries[out->valid_entries].value;
+
+	entry->chip = smp2p_gpio_chip;
+	entry->chip.base = -1;
+	entry->chip.ngpio = 32;
+	entry->chip.label = entry->name;
+	entry->chip.dev = smp2p->dev;
+	entry->chip.of_node = node;
+
+	ret = gpiochip_add(&entry->chip);
+	if (ret)
+		dev_err(smp2p->dev, "failed register gpiochip\n");
+
+	return 0;
+}
+
+static int qcom_smp2p_alloc_outbound_item(struct qcom_smp2p *smp2p)
+{
+	struct smp2p_smem_item *out;
+	unsigned smem_id = smp2p->smem_items[SMP2P_OUTBOUND];
+	unsigned pid = smp2p->remote_pid;
+	int ret;
+
+	ret = qcom_smem_alloc(pid, smem_id, sizeof(*out));
+	if (ret < 0 && ret != -EEXIST) {
+		if (ret != -EPROBE_DEFER)
+			dev_err(smp2p->dev,
+				"unable to allocate local smp2p item\n");
+		return ret;
+	}
+
+	ret = qcom_smem_get(pid, smem_id, (void **)&out, NULL);
+	if (ret < 0) {
+		dev_err(smp2p->dev, "Unable to acquire local smp2p item\n");
+		return ret;
+	}
+
+	memset(out, 0, sizeof(*out));
+	out->magic = SMP2P_MAGIC;
+	out->local_pid = smp2p->local_pid;
+	out->remote_pid = smp2p->remote_pid;
+	out->total_entries = SMP2P_MAX_ENTRY;
+	out->valid_entries = 0;
+
+	/*
+	 * Make sure the rest of the header is written before we validate the
+	 * item by writing a valid version number.
+	 */
+	wmb();
+	out->version = 1;
+
+	qcom_smp2p_kick(smp2p);
+
+	smp2p->out = out;
+
+	return 0;
+}
+
+static int smp2p_parse_ipc(struct qcom_smp2p *smp2p)
+{
+	struct device_node *syscon;
+	struct device *dev = smp2p->dev;
+	const char *key;
+	int ret;
+
+	syscon = of_parse_phandle(dev->of_node, "qcom,ipc", 0);
+	if (!syscon) {
+		dev_err(dev, "no qcom,ipc node\n");
+		return -ENODEV;
+	}
+
+	smp2p->ipc_regmap = syscon_node_to_regmap(syscon);
+	if (IS_ERR(smp2p->ipc_regmap))
+		return PTR_ERR(smp2p->ipc_regmap);
+
+	key = "qcom,ipc";
+	ret = of_property_read_u32_index(dev->of_node, key, 1, &smp2p->ipc_offset);
+	if (ret < 0) {
+		dev_err(dev, "no offset in %s\n", key);
+		return -EINVAL;
+	}
+
+	ret = of_property_read_u32_index(dev->of_node, key, 2, &smp2p->ipc_bit);
+	if (ret < 0) {
+		dev_err(dev, "no bit in %s\n", key);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int qcom_smp2p_probe(struct platform_device *pdev)
+{
+	struct smp2p_entry *entry;
+	struct device_node *node;
+	struct qcom_smp2p *smp2p;
+	const char *key;
+	int irq;
+	int ret;
+
+	smp2p = devm_kzalloc(&pdev->dev, sizeof(*smp2p), GFP_KERNEL);
+	if (!smp2p)
+		return -ENOMEM;
+
+	smp2p->dev = &pdev->dev;
+	INIT_LIST_HEAD(&smp2p->inbound);
+	INIT_LIST_HEAD(&smp2p->outbound);
+
+	platform_set_drvdata(pdev, smp2p);
+
+	ret = smp2p_parse_ipc(smp2p);
+	if (ret)
+		return ret;
+
+	key = "qcom,smem";
+	ret = of_property_read_u32_array(pdev->dev.of_node, key,
+					 smp2p->smem_items, 2);
+	if (ret)
+		return ret;
+
+	key = "qcom,local-pid";
+	ret = of_property_read_u32(pdev->dev.of_node, key, &smp2p->local_pid);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed to read %s\n", key);
+		return -EINVAL;
+	}
+
+	key = "qcom,remote-pid";
+	ret = of_property_read_u32(pdev->dev.of_node, key, &smp2p->remote_pid);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed to read %s\n", key);
+		return -EINVAL;
+	}
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(&pdev->dev, "unable to acquire smp2p interrupt\n");
+		return irq;
+	}
+
+	ret = qcom_smp2p_alloc_outbound_item(smp2p);
+	if (ret < 0)
+		return ret;
+
+	for_each_available_child_of_node(pdev->dev.of_node, node) {
+		entry = devm_kzalloc(&pdev->dev, sizeof(*entry), GFP_KERNEL);
+		if (!entry) {
+			ret = -ENOMEM;
+			goto unwind_interfaces;
+		}
+
+		entry->smp2p = smp2p;
+		spin_lock_init(&entry->lock);
+
+		ret = of_property_read_string(node, "qcom,entry-name", &entry->name);
+		if (ret < 0)
+			goto unwind_interfaces;
+
+		if (of_property_read_bool(node, "interrupt-controller")) {
+			ret = qcom_smp2p_inbound_entry(smp2p, entry, node);
+			if (ret < 0)
+				goto unwind_interfaces;
+
+			list_add(&entry->node, &smp2p->inbound);
+		} else if (of_property_read_bool(node, "gpio-controller")) {
+			ret = qcom_smp2p_outbound_entry(smp2p, entry, node);
+			if (ret < 0)
+				goto unwind_interfaces;
+
+			list_add(&entry->node, &smp2p->outbound);
+		} else {
+			dev_err(&pdev->dev, "neither inbound nor outbound\n");
+			ret = -EINVAL;
+			goto unwind_interfaces;
+		}
+	}
+
+	/* Kick the outgoing edge after allocating entries */
+	qcom_smp2p_kick(smp2p);
+
+	ret = devm_request_threaded_irq(&pdev->dev, irq,
+					NULL, qcom_smp2p_intr,
+					IRQF_ONESHOT,
+					"smp2p", (void *)smp2p);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to request interrupt\n");
+		goto unwind_interfaces;
+	}
+
+
+	return 0;
+
+unwind_interfaces:
+	list_for_each_entry(entry, &smp2p->inbound, node)
+		irq_domain_remove(entry->domain);
+
+	list_for_each_entry(entry, &smp2p->outbound, node)
+		gpiochip_remove(&entry->chip);
+
+	smp2p->out->valid_entries = 0;
+
+	return ret;
+}
+
+static int qcom_smp2p_remove(struct platform_device *pdev)
+{
+	struct qcom_smp2p *smp2p = platform_get_drvdata(pdev);
+	struct smp2p_entry *entry;
+
+	list_for_each_entry(entry, &smp2p->inbound, node)
+		irq_domain_remove(entry->domain);
+
+	list_for_each_entry(entry, &smp2p->outbound, node)
+		gpiochip_remove(&entry->chip);
+
+	smp2p->out->valid_entries = 0;
+
+	return 0;
+}
+
+static const struct of_device_id qcom_smp2p_of_match[] = {
+	{ .compatible = "qcom,smp2p" },
+	{}
+};
+MODULE_DEVICE_TABLE(of, qcom_smp2p_of_match);
+
+static struct platform_driver qcom_smp2p_driver = {
+	.probe = qcom_smp2p_probe,
+	.remove = qcom_smp2p_remove,
+	.driver  = {
+		.name  = "qcom_smp2p",
+		.of_match_table = qcom_smp2p_of_match,
+	},
+};
+module_platform_driver(qcom_smp2p_driver);
+
+MODULE_DESCRIPTION("Qualcomm Shared Memory Point to Point driver");
+MODULE_LICENSE("GPL v2");
-- 
1.8.2.2


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* Re: [PATCH 1/4] dt-binding: gpio: Add Qualcomm SMSM device tree documentation
  2015-08-27 17:37     ` Bjorn Andersson
  (?)
@ 2015-08-31 15:43     ` Rob Herring
  2015-09-01  5:26       ` Bjorn Andersson
  2015-09-08 12:53       ` Linus Walleij
  -1 siblings, 2 replies; 21+ messages in thread
From: Rob Herring @ 2015-08-31 15:43 UTC (permalink / raw)
  To: Bjorn Andersson
  Cc: Alexandre Courbot, Ian Campbell, Linus Walleij, Mark Rutland,
	Pawel Moll, Rob Herring, devicetree, linux-arm-msm, linux-gpio,
	linux-kernel

On Thu, Aug 27, 2015 at 12:37 PM, Bjorn Andersson
<bjorn.andersson@sonymobile.com> wrote:
> This documents a device tree binding for exposing the Qualcomm Shared
> Memory State Machine as a set of gpio- and interrupt-controllers.
>
> Signed-off-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
> ---
>  .../devicetree/bindings/gpio/qcom,smsm.txt         | 114 +++++++++++++++++++++

>  drivers/gpio/Kconfig                               |   8 ++
>  drivers/gpio/Makefile                              |   1 +

Presumably this goes in patch 2.

>  3 files changed, 123 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/gpio/qcom,smsm.txt
>
> diff --git a/Documentation/devicetree/bindings/gpio/qcom,smsm.txt b/Documentation/devicetree/bindings/gpio/qcom,smsm.txt
> new file mode 100644
> index 000000000000..06201ba76594
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/gpio/qcom,smsm.txt
> @@ -0,0 +1,114 @@
> +Qualcomm Shared Memory State Machine
> +
> +The Shared Memory State Machine facilitates broadcasting of single bit state
> +information between the processors in a Qualcomm SoC. Each processor is
> +assigned 32 bits of state that can be modified. A processor can through a
> +matrix of bitmaps signal subscription of notifications upon changes to a
> +certain bit owned by a certain remote processor.

Are all the bits s/w driven, but somehow fixed in their functional definition?

> +This document defines the binding for a driver that implements and exposes this
> +a GPIO controller and a set of interrupt controllers.

I imagine Linus will have thoughts about that.

> +
> +- compatible:
> +       Usage: required
> +       Value type: <string>
> +       Definition: must be one of:
> +                   "qcom,smsm"

There are not versions of the h/w?

> +
> +- qcom,ipc-N:
> +       Usage: required
> +       Value type: <prop-encoded-array>
> +       Definition: three entries specifying the outgoing ipc bit used for
> +                   signaling the N:th remote processor
> +                   - phandle to a syscon node representing the apcs registers
> +                   - u32 representing offset to the register within the syscon
> +                   - u32 representing the ipc bit within the register
> +
> +- qcom,local-host:
> +       Usage: optional
> +       Value type: <u32>
> +       Definition: identifier of the local processor in the list of hosts, or
> +                   in other words specifier of the column in the subscription
> +                   matrix representing the local processor
> +                   defaults to host 0
> +
> +- #address-cells:
> +       Usage: required
> +       Value type: <u32>
> +       Definition: must be 1
> +
> +- #size-cells:
> +       Usage: required
> +       Value type: <u32>
> +       Definition: must be 0
> +
> += SUBNODES
> +Each processor's state bits are described by a subnode of the smsm device node.
> +A node can either be a gpio-controller - denoting the local processors bits -
> +or an interrupt-controller - denoting a remote processors state bits.  The node
> +names are not important.
> +
> +- reg:
> +       Usage: required
> +       Value type: <u32>
> +       Definition: specifies the offset, in words, of the first bit for this
> +                   entry
> +
> +- gpio-controller:
> +       Usage: required for local entry
> +       Value type: <empty>
> +       Definition: marks the entry as a gpio-controller and the state bits to
> +                   belong to the local processor
> +
> +- #gpio-cells:
> +       Usage: required for local entry
> +       Value type: <u32>
> +       Definition: must be 2 - denotes bit number and GPIO flags
> +
> +- interrupt-controller:
> +       Usage: required for remote entries
> +       Value type: <empty>
> +       Definition: marks the entry as a interrupt-controller and the state bits
> +                   to belong to a remote processor
> +
> +- #interrupt-cells:
> +       Usage: required for remote entries
> +       Value type: <u32>
> +       Definition: must be 2 - denotes bit number and IRQ flags
> +
> +- interrupts:
> +       Usage: required for remote entries
> +       Value type: <prop-encoded-array>
> +       Definition: one entry specifying remote IRQ used by the remote processor
> +                   to signal changes of its state bits
> +
> +
> += EXAMPLE
> +The following example shows the SMEM setup for controlling properties of the
> +wireless processor, defined from the 8974 apps processor's point-of-view. It
> +encompasses one outbound entry and the outgoing interrupt for the wireless
> +processor.
> +
> +smsm {
> +       compatible = "qcom,smsm";
> +
> +       #address-cells = <1>;
> +       #size-cells = <0>;
> +
> +       qcom,ipc-3 = <&apcs 8 19>;
> +
> +       apps_smsm: apps@0 {
> +               reg = <0>;
> +
> +               gpio-controller;
> +               #gpio-cells = <2>;
> +       };
> +
> +       wcnss_smsm: wcnss@7 {
> +               reg = <7>;
> +               interrupts = <0 144 1>;
> +
> +               interrupt-controller;
> +               #interrupt-cells = <2>;
> +       };
> +};
> diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
> index b4fc9e4d24c6..0e57b60faae8 100644
> --- a/drivers/gpio/Kconfig
> +++ b/drivers/gpio/Kconfig
> @@ -354,6 +354,14 @@ config GPIO_PXA
>         help
>           Say yes here to support the PXA GPIO device
>
> +config GPIO_QCOM_SMSM
> +       bool "Qualcomm Shared Memory State Machine"
> +       depends on QCOM_SMEM
> +       help
> +         Say yes here to support the Qualcomm Shared Memory State Machine.
> +         The state machine is represented by bits in shared memory and is
> +         exposed to the system as GPIOs.
> +
>  config GPIO_RCAR
>         tristate "Renesas R-Car GPIO"
>         depends on ARM && (ARCH_SHMOBILE || COMPILE_TEST)
> diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
> index f79a7c482a99..0fd213892403 100644
> --- a/drivers/gpio/Makefile
> +++ b/drivers/gpio/Makefile
> @@ -75,6 +75,7 @@ obj-$(CONFIG_GPIO_PCF857X)    += gpio-pcf857x.o
>  obj-$(CONFIG_GPIO_PCH)         += gpio-pch.o
>  obj-$(CONFIG_GPIO_PL061)       += gpio-pl061.o
>  obj-$(CONFIG_GPIO_PXA)         += gpio-pxa.o
> +obj-$(CONFIG_GPIO_QCOM_SMSM)   += gpio-qcom-smsm.o
>  obj-$(CONFIG_GPIO_RC5T583)     += gpio-rc5t583.o
>  obj-$(CONFIG_GPIO_RDC321X)     += gpio-rdc321x.o
>  obj-$(CONFIG_GPIO_RCAR)                += gpio-rcar.o
> --
> 1.8.2.2
>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH 1/4] dt-binding: gpio: Add Qualcomm SMSM device tree documentation
  2015-08-31 15:43     ` Rob Herring
@ 2015-09-01  5:26       ` Bjorn Andersson
  2015-09-08 12:53       ` Linus Walleij
  1 sibling, 0 replies; 21+ messages in thread
From: Bjorn Andersson @ 2015-09-01  5:26 UTC (permalink / raw)
  To: Rob Herring
  Cc: Alexandre Courbot, Ian Campbell, Linus Walleij, Mark Rutland,
	Pawel Moll, Rob Herring, devicetree, linux-arm-msm, linux-gpio,
	linux-kernel

On Mon 31 Aug 08:43 PDT 2015, Rob Herring wrote:

> On Thu, Aug 27, 2015 at 12:37 PM, Bjorn Andersson
> <bjorn.andersson@sonymobile.com> wrote:
> > This documents a device tree binding for exposing the Qualcomm Shared
> > Memory State Machine as a set of gpio- and interrupt-controllers.
> >
> > Signed-off-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
> > ---
> >  .../devicetree/bindings/gpio/qcom,smsm.txt         | 114 +++++++++++++++++++++
> 
> >  drivers/gpio/Kconfig                               |   8 ++
> >  drivers/gpio/Makefile                              |   1 +
> 
> Presumably this goes in patch 2.
> 

Of course, my git-fu let me down.

> >  3 files changed, 123 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/gpio/qcom,smsm.txt
> >
> > diff --git a/Documentation/devicetree/bindings/gpio/qcom,smsm.txt b/Documentation/devicetree/bindings/gpio/qcom,smsm.txt
> > new file mode 100644
> > index 000000000000..06201ba76594
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/gpio/qcom,smsm.txt
> > @@ -0,0 +1,114 @@
> > +Qualcomm Shared Memory State Machine
> > +
> > +The Shared Memory State Machine facilitates broadcasting of single bit state
> > +information between the processors in a Qualcomm SoC. Each processor is
> > +assigned 32 bits of state that can be modified. A processor can through a
> > +matrix of bitmaps signal subscription of notifications upon changes to a
> > +certain bit owned by a certain remote processor.
> 
> Are all the bits s/w driven, but somehow fixed in their functional definition?
> 

There's a wide range of systems implementing this, but I think it's all
software. The definition of the individual bits used to be defined by
one large enum, but it looks like Qualcomm ran out of bits and started
to re-use them.

But the functions are always well defined for a given platform.

> > +This document defines the binding for a driver that implements and exposes this
> > +a GPIO controller and a set of interrupt controllers.
> 
> I imagine Linus will have thoughts about that.
> 

Looking forward to hearing his comments :)

I have not been able to find any other suitable framework to expose this
functionality with and I am hoping to not be forced to create a new one
as this one fits the purpose well.

> > +
> > +- compatible:
> > +       Usage: required
> > +       Value type: <string>
> > +       Definition: must be one of:
> > +                   "qcom,smsm"
> 
> There are not versions of the h/w?
> 

There are basically 2 versions; the original version comprised the
application cpu and the modem cpu, sometime around 2008-2009 this was
replaced with something called "N-way SMSM" - i.e. this implementation.
The only change since then is in the number of entries and number of
hosts - which we can determine in runtime if it's not compatible with
the original sizes.


So I have not been able to find any reasons to be more specific.


Thanks for taking a look.

Regards,
Bjorn

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH 1/4] dt-binding: gpio: Add Qualcomm SMSM device tree documentation
  2015-08-27 17:37     ` Bjorn Andersson
  (?)
  (?)
@ 2015-09-01  8:18     ` Stanimir Varbanov
       [not found]       ` <55E55F70.5050703-NEYub+7Iv8PQT0dZR+AlfA@public.gmane.org>
  -1 siblings, 1 reply; 21+ messages in thread
From: Stanimir Varbanov @ 2015-09-01  8:18 UTC (permalink / raw)
  To: Bjorn Andersson, Alexandre Courbot, Ian Campbell, Linus Walleij,
	Mark Rutland, Pawel Moll, Rob Herring
  Cc: devicetree, linux-arm-msm, linux-gpio, linux-kernel

On 08/27/2015 08:37 PM, Bjorn Andersson wrote:
> This documents a device tree binding for exposing the Qualcomm Shared
> Memory State Machine as a set of gpio- and interrupt-controllers.
> 
> Signed-off-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
> ---
>  .../devicetree/bindings/gpio/qcom,smsm.txt         | 114 +++++++++++++++++++++

<snip>

> += EXAMPLE
> +The following example shows the SMEM setup for controlling properties of the
> +wireless processor, defined from the 8974 apps processor's point-of-view. It
> +encompasses one outbound entry and the outgoing interrupt for the wireless
> +processor.
> +
> +smsm {
> +	compatible = "qcom,smsm";
> +
> +	#address-cells = <1>;
> +	#size-cells = <0>;
> +
> +	qcom,ipc-3 = <&apcs 8 19>;

Can we use something more descriptive here, for example

qcom,ipc-rpm = <&apcs 8 19>;

and replace these magic numbers with defines?

> +
> +	apps_smsm: apps@0 {
> +		reg = <0>;
> +
> +		gpio-controller;
> +		#gpio-cells = <2>;
> +	};
> +
> +	wcnss_smsm: wcnss@7 {
> +		reg = <7>;
> +		interrupts = <0 144 1>;
> +
> +		interrupt-controller;
> +		#interrupt-cells = <2>;
> +	};
> +};


-- 
regards,
Stan

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH 1/4] dt-binding: gpio: Add Qualcomm SMSM device tree documentation
  2015-09-01  8:18     ` Stanimir Varbanov
@ 2015-09-01 21:50           ` Bjorn Andersson
  0 siblings, 0 replies; 21+ messages in thread
From: Bjorn Andersson @ 2015-09-01 21:50 UTC (permalink / raw)
  To: Stanimir Varbanov
  Cc: Alexandre Courbot, Ian Campbell, Linus Walleij, Mark Rutland,
	Pawel Moll, Rob Herring, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-msm-u79uwXL29TY76Z2rM5mHXA,
	linux-gpio-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA

On Tue 01 Sep 01:18 PDT 2015, Stanimir Varbanov wrote:

> On 08/27/2015 08:37 PM, Bjorn Andersson wrote:
> > This documents a device tree binding for exposing the Qualcomm Shared
> > Memory State Machine as a set of gpio- and interrupt-controllers.
> > 
> > Signed-off-by: Bjorn Andersson <bjorn.andersson-/MT0OVThwyLZJqsBc5GL+g@public.gmane.org>
> > ---
> >  .../devicetree/bindings/gpio/qcom,smsm.txt         | 114 +++++++++++++++++++++
> 
> <snip>
> 
> > += EXAMPLE
> > +The following example shows the SMEM setup for controlling properties of the
> > +wireless processor, defined from the 8974 apps processor's point-of-view. It
> > +encompasses one outbound entry and the outgoing interrupt for the wireless
> > +processor.
> > +
> > +smsm {
> > +	compatible = "qcom,smsm";
> > +
> > +	#address-cells = <1>;
> > +	#size-cells = <0>;
> > +
> > +	qcom,ipc-3 = <&apcs 8 19>;
> 
> Can we use something more descriptive here, for example
> 
> qcom,ipc-rpm = <&apcs 8 19>;

Not really, because in the view of smsm we don't now which host the rpm
would represent. But most likely 3 would mean WCNSS, per the enum in
caf.

> 
> and replace these magic numbers with defines?
> 

I don't know what those defines would be named, what you have here is
bit 19 in the 8th byte - straight from the register documentation.

> > +
> > +	apps_smsm: apps@0 {
> > +		reg = <0>;
> > +
> > +		gpio-controller;
> > +		#gpio-cells = <2>;
> > +	};
> > +
> > +	wcnss_smsm: wcnss@7 {
> > +		reg = <7>;
> > +		interrupts = <0 144 1>;
> > +
> > +		interrupt-controller;
> > +		#interrupt-cells = <2>;
> > +	};
> > +};

Regards,
Bjorn
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH 1/4] dt-binding: gpio: Add Qualcomm SMSM device tree documentation
@ 2015-09-01 21:50           ` Bjorn Andersson
  0 siblings, 0 replies; 21+ messages in thread
From: Bjorn Andersson @ 2015-09-01 21:50 UTC (permalink / raw)
  To: Stanimir Varbanov
  Cc: Alexandre Courbot, Ian Campbell, Linus Walleij, Mark Rutland,
	Pawel Moll, Rob Herring, devicetree, linux-arm-msm, linux-gpio,
	linux-kernel

On Tue 01 Sep 01:18 PDT 2015, Stanimir Varbanov wrote:

> On 08/27/2015 08:37 PM, Bjorn Andersson wrote:
> > This documents a device tree binding for exposing the Qualcomm Shared
> > Memory State Machine as a set of gpio- and interrupt-controllers.
> > 
> > Signed-off-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
> > ---
> >  .../devicetree/bindings/gpio/qcom,smsm.txt         | 114 +++++++++++++++++++++
> 
> <snip>
> 
> > += EXAMPLE
> > +The following example shows the SMEM setup for controlling properties of the
> > +wireless processor, defined from the 8974 apps processor's point-of-view. It
> > +encompasses one outbound entry and the outgoing interrupt for the wireless
> > +processor.
> > +
> > +smsm {
> > +	compatible = "qcom,smsm";
> > +
> > +	#address-cells = <1>;
> > +	#size-cells = <0>;
> > +
> > +	qcom,ipc-3 = <&apcs 8 19>;
> 
> Can we use something more descriptive here, for example
> 
> qcom,ipc-rpm = <&apcs 8 19>;

Not really, because in the view of smsm we don't now which host the rpm
would represent. But most likely 3 would mean WCNSS, per the enum in
caf.

> 
> and replace these magic numbers with defines?
> 

I don't know what those defines would be named, what you have here is
bit 19 in the 8th byte - straight from the register documentation.

> > +
> > +	apps_smsm: apps@0 {
> > +		reg = <0>;
> > +
> > +		gpio-controller;
> > +		#gpio-cells = <2>;
> > +	};
> > +
> > +	wcnss_smsm: wcnss@7 {
> > +		reg = <7>;
> > +		interrupts = <0 144 1>;
> > +
> > +		interrupt-controller;
> > +		#interrupt-cells = <2>;
> > +	};
> > +};

Regards,
Bjorn

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH 0/4] Qualcomm Shared Memory State Machines
  2015-08-27 17:37 ` Bjorn Andersson
                   ` (3 preceding siblings ...)
  (?)
@ 2015-09-08 12:20 ` Linus Walleij
  2015-09-08 13:32   ` Mark Brown
       [not found]   ` <CACRpkdb-624MauE4sS0mqxarXRRaS3sEB2aMzeRNZw945xhMnA-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
  -1 siblings, 2 replies; 21+ messages in thread
From: Linus Walleij @ 2015-09-08 12:20 UTC (permalink / raw)
  To: Bjorn Andersson, Mark Brown
  Cc: Alexandre Courbot, Ian Campbell, Mark Rutland, Pawel Moll,
	Rob Herring, devicetree, linux-arm-msm, linux-gpio, linux-kernel

On Thu, Aug 27, 2015 at 7:37 PM, Bjorn Andersson
<bjorn.andersson@sonymobile.com> wrote:

Let's discuss this a bit, looping in Mark Brown.

> As some of these states on some platforms are passed as physical signals
> instead, the two drivers are modelled as gpio- and interrupt-controllers -
> providing a nice abstraction both in device tree sense and Linux implementation
> sense.

I have unfortunately repeatedly encountered an argument of the type
"GPIO has nice infrastructure so let's call X 'a kind of GPIO' so
we can use that nice infrastructure".

I'm not pleased with something that is not really general purpose
input/output being called GPIO. It fits badly with the GPIO subsystem
when we support things like open drain and cross-mappings to
the pin control subsystem that will never be applicable to these
new users. It's more like "special purpose input/output" or
something.

This happened with syscon LEDs for example, and was the reason
I implemented syscon-leds. The original proposal was to say the LEDs
register was "a kind of GPIO" on top of regmap and then use gpio-leds
on top of that. Instead I insisted it was a syscon/regmap and the
LEDs go directly to that, bypassing one layer of abstraction.

I also imagine creating syscon-keys.c for input from arbitrary keys
in a syscon register actually. Just didn't get around to that yet.

I also see some of the stuff GPIO does out-of-the-box being useful,
for example in MFD: some of these are interrupt controllers and
basically duplicate the kind of code I have in gpiolib for
GPIOLIB_IRQCHIP.

So we need to figure out what is really needed.

While sparsely documented: what about regmap (maybe in the
form of syscon) and drivers/base/regmap/regmap-irq.c for the
interrupt handling?

I cannot claim to understand regmap-irq.c, but I just intuitively
think it deserves consideration, such that drivers who need one
of these misc registers can read/write their bits with regmap
accessors and also get IRQs by way of regmap-irq.

Yours,
Linus Walleij

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH 1/4] dt-binding: gpio: Add Qualcomm SMSM device tree documentation
  2015-08-31 15:43     ` Rob Herring
  2015-09-01  5:26       ` Bjorn Andersson
@ 2015-09-08 12:53       ` Linus Walleij
  1 sibling, 0 replies; 21+ messages in thread
From: Linus Walleij @ 2015-09-08 12:53 UTC (permalink / raw)
  To: Rob Herring, Mark Brown
  Cc: Bjorn Andersson, Alexandre Courbot, Ian Campbell, Mark Rutland,
	Pawel Moll, Rob Herring, devicetree, linux-arm-msm, linux-gpio,
	linux-kernel

On Mon, Aug 31, 2015 at 5:43 PM, Rob Herring <robherring2@gmail.com> wrote:
> On Thu, Aug 27, 2015 at 12:37 PM, Bjorn Andersson <bjorn.andersson@sonymobile.com> wrote:

>> This documents a device tree binding for exposing the Qualcomm Shared
>> Memory State Machine as a set of gpio- and interrupt-controllers.
>>
>> Signed-off-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>

>> +This document defines the binding for a driver that implements and exposes this
>> +a GPIO controller and a set of interrupt controllers.
>
> I imagine Linus will have thoughts about that.

Yeah you bet :D

I wrote a lengthy answer to patch 0.

Point being: if we insist on this being modeled as "a kind of GPIO", then
*BSD and Windows also has to think of it as "a kind of GPIO" meaning
we put Linux implementation details into the DT bindings.

At least the idea Rob had about register-bit-* bindings should be respected
see for example:
Documentation/devicetree/bindings/leds/register-bit-led.txt

That naming is neutral, even if we end up solving it in Linux with a GPIO
abstraction, it doesn't enforce that on $OTHER_OS.

Yours,
Linus Walleij

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH 2/4] gpio: qcom-smsm: Add driver for Qualcomm SMSM
  2015-08-27 17:37   ` Bjorn Andersson
  (?)
@ 2015-09-08 12:58   ` Linus Walleij
  -1 siblings, 0 replies; 21+ messages in thread
From: Linus Walleij @ 2015-09-08 12:58 UTC (permalink / raw)
  To: Bjorn Andersson
  Cc: Alexandre Courbot, Ian Campbell, Mark Rutland, Pawel Moll,
	Rob Herring, devicetree, linux-arm-msm, linux-gpio, linux-kernel

On Thu, Aug 27, 2015 at 7:37 PM, Bjorn Andersson
<bjorn.andersson@sonymobile.com> wrote:

> This driver exposed the Qualcomm Shared Memory State Machine bits as
> GPIOs to the system.
>
> Signed-off-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
(...)
> +/*
> + * This driver implements the Qualcomm Shared Memory State Machine, a mechanism
> + * for communicating single bit state information to remote processors.
> + *
> + * The implementation is based on two sections of shared memory; the first
> + * holding the state bits and the second holding a matrix of subscription bits.
> + *
> + * The state bits are structured in entries of 32 bits, each belonging to one
> + * system in the SoC. The entry belonging to the local system is considered
> + * read-write, while the rest should be considered read-only.
> + *
> + * The subscription matrix consists of N bitmaps per entry, denoting interest
> + * in updates of the entry for each of the N hosts. Upon updating a state bit
> + * each host's subscription bitmap should be queried and the remote system
> + * should be interrupted if they request so.
> + *
> + * The subscription matrix is laid out in entry-major order:
> + * entry0: [host0 ... hostN]
> + *     .
> + *     .
> + * entryM: [host0 ... hostN]
> + *
> + * A third, optional, shared memory region might contain information regarding
> + * the number of entries in the state bitmap as well as number of columns in
> + * the subscription matrix.
> + */

This does seem like a real bad fit to GPIO for me, sorry.

IMO this is drivers/soc/qcom, using regmap and possibly
regmap IRQs-material.

Yours,
Linus Walleij

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH 0/4] Qualcomm Shared Memory State Machines
  2015-09-08 12:20 ` [PATCH 0/4] Qualcomm Shared Memory State Machines Linus Walleij
@ 2015-09-08 13:32   ` Mark Brown
       [not found]   ` <CACRpkdb-624MauE4sS0mqxarXRRaS3sEB2aMzeRNZw945xhMnA-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
  1 sibling, 0 replies; 21+ messages in thread
From: Mark Brown @ 2015-09-08 13:32 UTC (permalink / raw)
  To: Linus Walleij
  Cc: Bjorn Andersson, Alexandre Courbot, Ian Campbell, Mark Rutland,
	Pawel Moll, Rob Herring, devicetree, linux-arm-msm, linux-gpio,
	linux-kernel

[-- Attachment #1: Type: text/plain, Size: 1130 bytes --]

On Tue, Sep 08, 2015 at 02:20:14PM +0200, Linus Walleij wrote:
> On Thu, Aug 27, 2015 at 7:37 PM, Bjorn Andersson
> <bjorn.andersson@sonymobile.com> wrote:

> Let's discuss this a bit, looping in Mark Brown.

Please don't send upstream discussions ot my work address.

> While sparsely documented: what about regmap (maybe in the
> form of syscon) and drivers/base/regmap/regmap-irq.c for the
> interrupt handling?

> I cannot claim to understand regmap-irq.c, but I just intuitively
> think it deserves consideration, such that drivers who need one
> of these misc registers can read/write their bits with regmap
> accessors and also get IRQs by way of regmap-irq.

Well, all it's doing is exporting a simple register based interrupt
controller based on regmap.  There's not a lot to document.  If you're
using it with a system controller you'd probably want to implement
support for handling interrupts directly in the primary handler rather
than using a threaded handler.  Alternatively if there's no cache or no
overlap between interrupt registers and other registers then just using
the genirq MMIO code should do the trick.

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 473 bytes --]

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH 0/4] Qualcomm Shared Memory State Machines
  2015-09-08 12:20 ` [PATCH 0/4] Qualcomm Shared Memory State Machines Linus Walleij
@ 2015-09-08 22:22       ` Bjorn Andersson
       [not found]   ` <CACRpkdb-624MauE4sS0mqxarXRRaS3sEB2aMzeRNZw945xhMnA-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
  1 sibling, 0 replies; 21+ messages in thread
From: Bjorn Andersson @ 2015-09-08 22:22 UTC (permalink / raw)
  To: Linus Walleij
  Cc: Mark Brown, Alexandre Courbot, Ian Campbell, Mark Rutland,
	Pawel Moll, Rob Herring, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-msm-u79uwXL29TY76Z2rM5mHXA,
	linux-gpio-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA

On Tue 08 Sep 05:20 PDT 2015, Linus Walleij wrote:

> On Thu, Aug 27, 2015 at 7:37 PM, Bjorn Andersson
> <bjorn.andersson-/MT0OVThwyLZJqsBc5GL+g@public.gmane.org> wrote:
> 
> Let's discuss this a bit, looping in Mark Brown.
> 
> > As some of these states on some platforms are passed as physical signals
> > instead, the two drivers are modelled as gpio- and interrupt-controllers -
> > providing a nice abstraction both in device tree sense and Linux implementation
> > sense.
> 
> I have unfortunately repeatedly encountered an argument of the type
> "GPIO has nice infrastructure so let's call X 'a kind of GPIO' so
> we can use that nice infrastructure".
> 

I guess that's what you get for maintaining a subsystem with "general
purpose" in the name ;)

Sorry for adding one more item to your list.

> I'm not pleased with something that is not really general purpose
> input/output being called GPIO. It fits badly with the GPIO subsystem
> when we support things like open drain and cross-mappings to
> the pin control subsystem that will never be applicable to these
> new users. It's more like "special purpose input/output" or
> something.
> 

Right, this doesn't match at all with the electrical properties.

What it does give is the nice symmetry with the irq bits and existing
apis for poking and peeking.

> This happened with syscon LEDs for example, and was the reason
> I implemented syscon-leds. The original proposal was to say the LEDs
> register was "a kind of GPIO" on top of regmap and then use gpio-leds
> on top of that. Instead I insisted it was a syscon/regmap and the
> LEDs go directly to that, bypassing one layer of abstraction.
> 
> I also imagine creating syscon-keys.c for input from arbitrary keys
> in a syscon register actually. Just didn't get around to that yet.
> 

The difference with those two cases is that the pieces didn't fit
together, so we tried to use the gpio framework as an adaptor to make it
work.

I picked the gpio framework because:
* the DT nicely represent the structure of the memory and relationship
  between the individual components.

* the implementation needs to be able to set and clear individual bits,
  as well as register handlers for state changes.

* I'm told the SMP2P entries are on some platform (I've never seen)
  actual gpio lines.

> I also see some of the stuff GPIO does out-of-the-box being useful,
> for example in MFD: some of these are interrupt controllers and
> basically duplicate the kind of code I have in gpiolib for
> GPIOLIB_IRQCHIP.
> 

I do learn about new helper functions every time I touch these things.

> So we need to figure out what is really needed.
> 
> While sparsely documented: what about regmap (maybe in the
> form of syscon) and drivers/base/regmap/regmap-irq.c for the
> interrupt handling?
> 

regmap-irq looks good and reasonable.

The problem is that for SMSM I need custom masking operations and on
SMP2P I have multiple regmaps sharing one incoming IRQ. So it doesn't
look applicable for my use cases.

> I cannot claim to understand regmap-irq.c, but I just intuitively
> think it deserves consideration, such that drivers who need one
> of these misc registers can read/write their bits with regmap
> accessors and also get IRQs by way of regmap-irq.
> 

I was expecting some push back from you, but as it was the cleanest
abstraction I would come up with I gave it a shot :)

I'll rewrite the outgoing part based on regmaps instead. Thanks for
reviewing.

Regards,
Bjorn
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH 0/4] Qualcomm Shared Memory State Machines
@ 2015-09-08 22:22       ` Bjorn Andersson
  0 siblings, 0 replies; 21+ messages in thread
From: Bjorn Andersson @ 2015-09-08 22:22 UTC (permalink / raw)
  To: Linus Walleij
  Cc: Mark Brown, Alexandre Courbot, Ian Campbell, Mark Rutland,
	Pawel Moll, Rob Herring, devicetree, linux-arm-msm, linux-gpio,
	linux-kernel

On Tue 08 Sep 05:20 PDT 2015, Linus Walleij wrote:

> On Thu, Aug 27, 2015 at 7:37 PM, Bjorn Andersson
> <bjorn.andersson@sonymobile.com> wrote:
> 
> Let's discuss this a bit, looping in Mark Brown.
> 
> > As some of these states on some platforms are passed as physical signals
> > instead, the two drivers are modelled as gpio- and interrupt-controllers -
> > providing a nice abstraction both in device tree sense and Linux implementation
> > sense.
> 
> I have unfortunately repeatedly encountered an argument of the type
> "GPIO has nice infrastructure so let's call X 'a kind of GPIO' so
> we can use that nice infrastructure".
> 

I guess that's what you get for maintaining a subsystem with "general
purpose" in the name ;)

Sorry for adding one more item to your list.

> I'm not pleased with something that is not really general purpose
> input/output being called GPIO. It fits badly with the GPIO subsystem
> when we support things like open drain and cross-mappings to
> the pin control subsystem that will never be applicable to these
> new users. It's more like "special purpose input/output" or
> something.
> 

Right, this doesn't match at all with the electrical properties.

What it does give is the nice symmetry with the irq bits and existing
apis for poking and peeking.

> This happened with syscon LEDs for example, and was the reason
> I implemented syscon-leds. The original proposal was to say the LEDs
> register was "a kind of GPIO" on top of regmap and then use gpio-leds
> on top of that. Instead I insisted it was a syscon/regmap and the
> LEDs go directly to that, bypassing one layer of abstraction.
> 
> I also imagine creating syscon-keys.c for input from arbitrary keys
> in a syscon register actually. Just didn't get around to that yet.
> 

The difference with those two cases is that the pieces didn't fit
together, so we tried to use the gpio framework as an adaptor to make it
work.

I picked the gpio framework because:
* the DT nicely represent the structure of the memory and relationship
  between the individual components.

* the implementation needs to be able to set and clear individual bits,
  as well as register handlers for state changes.

* I'm told the SMP2P entries are on some platform (I've never seen)
  actual gpio lines.

> I also see some of the stuff GPIO does out-of-the-box being useful,
> for example in MFD: some of these are interrupt controllers and
> basically duplicate the kind of code I have in gpiolib for
> GPIOLIB_IRQCHIP.
> 

I do learn about new helper functions every time I touch these things.

> So we need to figure out what is really needed.
> 
> While sparsely documented: what about regmap (maybe in the
> form of syscon) and drivers/base/regmap/regmap-irq.c for the
> interrupt handling?
> 

regmap-irq looks good and reasonable.

The problem is that for SMSM I need custom masking operations and on
SMP2P I have multiple regmaps sharing one incoming IRQ. So it doesn't
look applicable for my use cases.

> I cannot claim to understand regmap-irq.c, but I just intuitively
> think it deserves consideration, such that drivers who need one
> of these misc registers can read/write their bits with regmap
> accessors and also get IRQs by way of regmap-irq.
> 

I was expecting some push back from you, but as it was the cleanest
abstraction I would come up with I gave it a shot :)

I'll rewrite the outgoing part based on regmaps instead. Thanks for
reviewing.

Regards,
Bjorn

^ permalink raw reply	[flat|nested] 21+ messages in thread

end of thread, other threads:[~2015-09-08 22:22 UTC | newest]

Thread overview: 21+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-08-27 17:37 [PATCH 0/4] Qualcomm Shared Memory State Machines Bjorn Andersson
2015-08-27 17:37 ` Bjorn Andersson
     [not found] ` <1440697078-4106-1-git-send-email-bjorn.andersson-/MT0OVThwyLZJqsBc5GL+g@public.gmane.org>
2015-08-27 17:37   ` [PATCH 1/4] dt-binding: gpio: Add Qualcomm SMSM device tree documentation Bjorn Andersson
2015-08-27 17:37     ` Bjorn Andersson
2015-08-31 15:43     ` Rob Herring
2015-09-01  5:26       ` Bjorn Andersson
2015-09-08 12:53       ` Linus Walleij
2015-09-01  8:18     ` Stanimir Varbanov
     [not found]       ` <55E55F70.5050703-NEYub+7Iv8PQT0dZR+AlfA@public.gmane.org>
2015-09-01 21:50         ` Bjorn Andersson
2015-09-01 21:50           ` Bjorn Andersson
2015-08-27 17:37   ` [PATCH 4/4] gpio: qcom-smp2p: Qualcomm Shared Memory Point to Point Bjorn Andersson
2015-08-27 17:37     ` Bjorn Andersson
2015-08-27 17:37 ` [PATCH 2/4] gpio: qcom-smsm: Add driver for Qualcomm SMSM Bjorn Andersson
2015-08-27 17:37   ` Bjorn Andersson
2015-09-08 12:58   ` Linus Walleij
2015-08-27 17:37 ` [PATCH 3/4] dt-binding: gpio: Introduce qcom,smp2p binding documentation Bjorn Andersson
2015-08-27 17:37   ` Bjorn Andersson
2015-09-08 12:20 ` [PATCH 0/4] Qualcomm Shared Memory State Machines Linus Walleij
2015-09-08 13:32   ` Mark Brown
     [not found]   ` <CACRpkdb-624MauE4sS0mqxarXRRaS3sEB2aMzeRNZw945xhMnA-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2015-09-08 22:22     ` Bjorn Andersson
2015-09-08 22:22       ` Bjorn Andersson

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.