linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC PATCH 0/8] firmware: ARM System Control and Management Interface(SCMI) support
@ 2017-06-07 16:10 Sudeep Holla
  2017-06-07 16:10 ` [RFC PATCH 1/8] Documentation: add DT binding for ARM System Control and Management Interface(SCMI) protocol Sudeep Holla
                   ` (7 more replies)
  0 siblings, 8 replies; 23+ messages in thread
From: Sudeep Holla @ 2017-06-07 16:10 UTC (permalink / raw)
  To: linux-kernel, devicetree
  Cc: Sudeep Holla, Roy Franz, Harb Abdulhamid, Nishanth Menon, Arnd Bergmann

Hi all,

Let me begin admitting that we are introducing yet another protocol to
achieve same things as many existing protocols like ARM SCPI, TI SCI,
QCOM RPM, Nvidia Tegra BPMP, and so on.

All I can say is that this new ARM System Control and Management
Interface(SCMI) is more flexible and easily extensible than any of the
existing ones. Many vendors were involved in the making of this formal
specification and is now officially published[1].

There is a strong trend in the industry to provide micro-controllers in
systems to abstract various power, or other system management tasks.
These controllers usually have similar interfaces, both in terms of the
functions that are provided by them, and in terms of how requests are
communicated to them.

This specification is to standardise and avoid (any further)
fragmentation in the design of such interface by various vendors.

This patch set is intended to get feedback on the design and structure
of the code. This is not complete and not fully tested due to
non-availability of firmware with full feature set at this time.

It currently doesn't support notification, asynchronous/delayed response,
perf/power statistics region and sensor register region to name a few.
I have borrowed some of the ideas of message allocation/management from
TI SCI.

--
Regards,
Sudeep

[1] http://infocenter.arm.com/help/topic/com.arm.doc.den0056a/index.html

Sudeep Holla (8):
  Documentation: add DT binding for ARM System Control and Management
    Interface(SCMI) protocol
  firmware: arm_scmi: add basic driver infrastructure for SCMI
  firmware: arm_scmi: add common infrastructure and support for base
    protocol
  firmware: arm_scmi: add initial support for performance protocol
  firmware: arm_scmi: add initial support for clock protocol
  firmware: arm_scmi: add initial support for power protocol
  firmware: arm_scmi: add initial support for sensor protocol
  firmware: arm_scmi: probe and initialise all the supported protocols

 Documentation/devicetree/bindings/arm/arm,scmi.txt | 193 +++++
 drivers/firmware/Kconfig                           |  21 +
 drivers/firmware/Makefile                          |   1 +
 drivers/firmware/arm_scmi/Makefile                 |   2 +
 drivers/firmware/arm_scmi/base.c                   | 290 +++++++
 drivers/firmware/arm_scmi/clock.c                  | 340 +++++++++
 drivers/firmware/arm_scmi/common.h                 | 127 ++++
 drivers/firmware/arm_scmi/driver.c                 | 832 +++++++++++++++++++++
 drivers/firmware/arm_scmi/perf.c                   | 398 ++++++++++
 drivers/firmware/arm_scmi/power.c                  | 237 ++++++
 drivers/firmware/arm_scmi/sensors.c                | 269 +++++++
 include/linux/scmi_protocol.h                      | 160 ++++
 12 files changed, 2870 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/arm/arm,scmi.txt
 create mode 100644 drivers/firmware/arm_scmi/Makefile
 create mode 100644 drivers/firmware/arm_scmi/base.c
 create mode 100644 drivers/firmware/arm_scmi/clock.c
 create mode 100644 drivers/firmware/arm_scmi/common.h
 create mode 100644 drivers/firmware/arm_scmi/driver.c
 create mode 100644 drivers/firmware/arm_scmi/perf.c
 create mode 100644 drivers/firmware/arm_scmi/power.c
 create mode 100644 drivers/firmware/arm_scmi/sensors.c
 create mode 100644 include/linux/scmi_protocol.h

--
2.7.4

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

* [RFC PATCH 1/8] Documentation: add DT binding for ARM System Control and Management Interface(SCMI) protocol
  2017-06-07 16:10 [RFC PATCH 0/8] firmware: ARM System Control and Management Interface(SCMI) support Sudeep Holla
@ 2017-06-07 16:10 ` Sudeep Holla
  2017-06-09 14:16   ` Rob Herring
       [not found]   ` <CAHCPf3s3MsiQyWFOgNJdD9F2JAwi_BVxVZG69zj+bJLzEw9AiA@mail.gmail.com>
  2017-06-07 16:10 ` [RFC PATCH 2/8] firmware: arm_scmi: add basic driver infrastructure for SCMI Sudeep Holla
                   ` (6 subsequent siblings)
  7 siblings, 2 replies; 23+ messages in thread
From: Sudeep Holla @ 2017-06-07 16:10 UTC (permalink / raw)
  To: linux-kernel, devicetree
  Cc: Sudeep Holla, Roy Franz, Harb Abdulhamid, Nishanth Menon,
	Arnd Bergmann, Rob Herring, Mark Rutland

This patch adds devicetree binding for System Control and Management
Interface (SCMI) Message Protocol used between the Application Cores(AP)
and the System Control Processor(SCP). The MHU peripheral provides a
mechanism for inter-processor communication between SCP's M3 processor
and AP.

SCP offers control and management of the core/cluster power states,
various power domain DVFS including the core/cluster, certain system
clocks configuration, thermal sensors and many others.

SCMI protocol is developed as better replacement to the existing SCPI
which is not flexible and easily extensible.

Cc: Rob Herring <robh+dt@kernel.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
---
 Documentation/devicetree/bindings/arm/arm,scmi.txt | 193 +++++++++++++++++++++
 1 file changed, 193 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/arm/arm,scmi.txt

diff --git a/Documentation/devicetree/bindings/arm/arm,scmi.txt b/Documentation/devicetree/bindings/arm/arm,scmi.txt
new file mode 100644
index 000000000000..d6e4b7eff199
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/arm,scmi.txt
@@ -0,0 +1,193 @@
+System Control and Management Interface (SCMI) Message Protocol
+----------------------------------------------------------
+
+The SCMI is intended to allow agents such as OSPM to manage various functions
+that are provided by the hardware platform it is running on, including power
+and performance functions.
+
+This binding is intended to define the interface the firmware implementing
+the SCMI as described in ARM document number ARM DUI 0922B ("ARM System Control
+and Management Interface Platform Design Document")[0] provide for OSPM in
+the device tree.
+
+Required properties:
+
+- compatible : shall be "arm,scmi"
+- method : The method of calling the SCMI firmware. Only permitted value
+	   currently is:
+	   "mailbox-doorbell" : When mailbox doorbell is used as a mechanism
+				to alert the presence of a messages and/or
+				notification
+- mboxes: List of phandle and mailbox channel specifiers. It should contain
+	  exactly one or two mailboxes, one for transmitting messages("tx")
+	  and another optional for receiving the notifications("rx") if
+	  supported.
+- mbox-names: shall be "tx" or "rx"
+- shmem : List of phandle pointing to the shared memory(SHM) area between the
+	  processors using these mailboxes for IPC, one for each mailbox
+	  SHM can be any memory reserved for the purpose of this communication
+	  between the processors.
+
+See Documentation/devicetree/bindings/mailbox/mailbox.txt for more details
+about the generic mailbox controller and client driver bindings.
+
+Each protocol supported shall have a sub-node with corresponding compatible
+as described in the following sections. If the platform supports dedicated
+communication channel for a particular protocol, the 3 properties namely:
+mboxes, mbox-names and shmem shall be present in the sub-node corresponding
+to that protocol.
+
+Clock/Performance bindings for the clocks/OPPs based on SCMI Message Protocol
+------------------------------------------------------------
+
+This binding uses the common clock binding[1].
+
+Required properties:
+- compatible : shall be "arm,scmi-clocks" or "arm,scmi-perf-domains"
+	arm,scmi-clocks:
+	       These clocks either provide an entire range of values
+	       between the limits or only discrete points each at fixed
+	       step size between the limits. The firmware provides
+	       mechanism to discover them.
+	arm,scmi-perf-domains:
+		These are OPPs(not just simple clocks), i.e. discrete
+		performance levels that are supported by the platform.
+		Again the firmware provides mechanism to discover the
+		performance and other attributes associated with the
+		levels.
+
+Other required properties for all clocks(all from common clock binding):
+- #clock-cells : Should be 1. Contains the Clock ID value used by SCMI commands.
+- clock-indices: The identifying number for the clocks(i.e.clock_id) in the
+	node. It can be non linear and hence provide the mapping of
+	identifiers.
+
+Power domain bindings for the power domains based on SCMI Message Protocol
+------------------------------------------------------------
+
+This binding uses the generic power domain binding[4].
+
+PM domain providers
+===================
+
+Required properties:
+ - #power-domain-cells : Should be 1. Contains the device or the power
+			 domain ID value used by SCMI commands.
+
+PM domain consumers
+===================
+
+Required properties:
+ - power-domains : A phandle and PM domain specifier as defined by bindings of
+                   the power controller specified by phandle.
+
+Sensor bindings for the sensors based on SCMI Message Protocol
+--------------------------------------------------------------
+SCMI provides an API to access the various sensors on the SoC.
+
+Required properties:
+- compatible : should be "arm,scmi-sensors".
+- #thermal-sensor-cells: should be set to 1. This property follows the
+			 thermal device tree bindings[2].
+
+			 Valid cell values are raw identifiers (Sensor ID)
+			 as used by the firmware. Refer to  platform details
+			 for your implementation for the IDs to use.
+
+SRAM and Shared Memory for SCMI
+-------------------------------
+
+A small area of SRAM is reserved for SCMI communication between application
+processors and SCP.
+
+The properties should follow the generic mmio-sram description found in [3]
+
+Each sub-node represents the reserved area for SCMI.
+
+Required sub-node properties:
+- reg : The base offset and size of the reserved area with the SRAM
+- compatible : should be "arm,scp-shmem" for Non-secure SRAM based
+	       shared memory
+
+[0] http://infocenter.arm.com/help/topic/com.arm.doc.den0056a/index.html
+[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
+[2] Documentation/devicetree/bindings/thermal/thermal.txt
+[3] Documentation/devicetree/bindings/sram/sram.txt
+[4] Documentation/devicetree/bindings/power/power_domain.txt
+
+Example:
+
+sram: sram@50000000 {
+	compatible = "arm,juno-sram-ns", "mmio-sram";
+	reg = <0x0 0x50000000 0x0 0x10000>;
+
+	#address-cells = <1>;
+	#size-cells = <1>;
+	ranges = <0 0x0 0x50000000 0x10000>;
+
+	cpu_scp_lpri: scp-shmem@0 {
+		compatible = "arm,juno-scp-shmem";
+		reg = <0x0 0x200>;
+	};
+
+	cpu_scp_hpri: scp-shmem@200 {
+		compatible = "arm,juno-scp-shmem";
+		reg = <0x200 0x200>;
+	};
+};
+
+mailbox: mailbox0@40000000 {
+	....
+	#mbox-cells = <1>;
+};
+
+scmi_protocol: scmi@2e000000 {
+	compatible = "arm,scmi";
+	mboxes = <&mailbox 0 &mailbox 1>;
+	shmem = <&cpu_scp_lpri &cpu_scp_hpri>;
+
+	scmi_dvfs: clocks@0 {
+		compatible = "arm,scmi-perf-domains";
+		#clock-cells = <1>;
+		clock-indices = <0>, <1>, <2>;
+	};
+	scmi_clk: clocks@3 {
+		compatible = "arm,scmi-clocks";
+		#clock-cells = <1>;
+		clock-indices = <3>, <4>;
+	};
+
+	scmi_sensors: sensors {
+		compatible = "arm,scmi-sensors";
+		#thermal-sensor-cells = <1>;
+	};
+
+	scmi_devpd: power-domains {
+		compatible = "arm,scmi-power-domains";
+		#power-domain-cells = <1>;
+	};
+};
+
+cpu@0 {
+	...
+	reg = <0 0>;
+	clocks = <&scmi_dvfs 0>;
+};
+
+hdlcd@7ff60000 {
+	...
+	reg = <0 0x7ff60000 0 0x1000>;
+	clocks = <&scmi_clk 4>;
+	power-domains = <&scmi_devpd 1>;
+};
+
+thermal-zones {
+	soc_thermal {
+		polling-delay-passive = <100>;
+		polling-delay = <1000>;
+
+				/* sensor         ID */
+		thermal-sensors = <&scmi_sensors0 3>;
+		...
+	};
+};
-- 
2.7.4

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

* [RFC PATCH 2/8] firmware: arm_scmi: add basic driver infrastructure for SCMI
  2017-06-07 16:10 [RFC PATCH 0/8] firmware: ARM System Control and Management Interface(SCMI) support Sudeep Holla
  2017-06-07 16:10 ` [RFC PATCH 1/8] Documentation: add DT binding for ARM System Control and Management Interface(SCMI) protocol Sudeep Holla
@ 2017-06-07 16:10 ` Sudeep Holla
  2017-06-07 19:18   ` Roy Franz
  2017-06-07 16:10 ` [RFC PATCH 3/8] firmware: arm_scmi: add common infrastructure and support for base protocol Sudeep Holla
                   ` (5 subsequent siblings)
  7 siblings, 1 reply; 23+ messages in thread
From: Sudeep Holla @ 2017-06-07 16:10 UTC (permalink / raw)
  To: linux-kernel, devicetree
  Cc: Sudeep Holla, Roy Franz, Harb Abdulhamid, Nishanth Menon, Arnd Bergmann

The SCMI is intended to allow OSPM to manage various functions that are
provided by the hardware platform it is running on, including power and
performance functions. SCMI provides two levels of abstraction, protocols
and transports. Protocols define individual groups of system control and
management messages. A protocol specification describes the messages
that it supports. Transports describe the method by which protocol
messages are communicated between agents and the platform.

This patch adds basic infrastructure to manage the message allocation,
initialisation, packing/unpacking and shared memory management.

Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
---
 drivers/firmware/Kconfig           |  21 ++
 drivers/firmware/Makefile          |   1 +
 drivers/firmware/arm_scmi/Makefile |   2 +
 drivers/firmware/arm_scmi/common.h |  74 ++++
 drivers/firmware/arm_scmi/driver.c | 737 +++++++++++++++++++++++++++++++++++++
 include/linux/scmi_protocol.h      |  48 +++
 6 files changed, 883 insertions(+)
 create mode 100644 drivers/firmware/arm_scmi/Makefile
 create mode 100644 drivers/firmware/arm_scmi/common.h
 create mode 100644 drivers/firmware/arm_scmi/driver.c
 create mode 100644 include/linux/scmi_protocol.h

diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
index 6e4ed5a9c6fd..c3d1a12763ce 100644
--- a/drivers/firmware/Kconfig
+++ b/drivers/firmware/Kconfig
@@ -19,6 +19,27 @@ config ARM_PSCI_CHECKER
 	  on and off through hotplug, so for now torture tests and PSCI checker
 	  are mutually exclusive.
 
+config ARM_SCMI_PROTOCOL
+	tristate "ARM System Control and Management Interface (SCMI) Message Protocol"
+	depends on ARM || ARM64 || COMPILE_TEST
+	depends on MAILBOX
+	help
+	  ARM System Control and Management Interface (SCMI) protocol is a
+	  set of operating system-independent software interfaces that are
+	  used in system management. SCMI is extensible and currently provides
+	  interfaces for: Discovery and self-description of the interfaces
+	  it supports, Power domain management which is the ability to place
+	  a given device or domain into the various power-saving states that
+	  it supports, Performance management which is the ability to control
+	  the performance of a domain that is composed of compute engines
+	  such as application processors and other accelerators, Clock
+	  management which is the ability to set and inquire rates on platform
+	  managed clocks and Sensor management which is the ability to read
+	  sensor data, and be notified of sensor value.
+
+	  This protocol library provides interface for all the client drivers
+	  making use of the features offered by the SCMI.
+
 config ARM_SCPI_PROTOCOL
 	tristate "ARM System Control and Power Interface (SCPI) Message Protocol"
 	depends on ARM || ARM64 || COMPILE_TEST
diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile
index a37f12e8d137..91d3ff62c653 100644
--- a/drivers/firmware/Makefile
+++ b/drivers/firmware/Makefile
@@ -23,6 +23,7 @@ obj-$(CONFIG_QCOM_SCM_32)	+= qcom_scm-32.o
 CFLAGS_qcom_scm-32.o :=$(call as-instr,.arch armv7-a\n.arch_extension sec,-DREQUIRES_SEC=1) -march=armv7-a
 obj-$(CONFIG_TI_SCI_PROTOCOL)	+= ti_sci.o
 
+obj-$(CONFIG_ARM_SCMI_PROTOCOL)	+= arm_scmi/
 obj-y				+= broadcom/
 obj-y				+= meson/
 obj-$(CONFIG_GOOGLE_FIRMWARE)	+= google/
diff --git a/drivers/firmware/arm_scmi/Makefile b/drivers/firmware/arm_scmi/Makefile
new file mode 100644
index 000000000000..58e94c95e523
--- /dev/null
+++ b/drivers/firmware/arm_scmi/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_ARM_SCMI_PROTOCOL)	= arm_scmi.o
+arm_scmi-y = driver.o
diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h
new file mode 100644
index 000000000000..a3038efa3a8d
--- /dev/null
+++ b/drivers/firmware/arm_scmi/common.h
@@ -0,0 +1,74 @@
+/*
+ * System Control and Management Interface (SCMI) Message Protocol
+ * driver common header file containing some definitions, structures
+ * and function prototypes used in all the different SCMI protocols.
+ *
+ * Copyright (C) 2017 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/completion.h>
+#include <linux/scmi_protocol.h>
+#include <linux/types.h>
+
+/**
+ * struct scmi_msg_hdr - Message(Tx/Rx) header
+ *
+ * @id: The identifier of the command being sent
+ * @protocol_id: The identifier of the protocol used to send @id command
+ * @seq: The token to identify the message. when a message/command returns,
+ *       the platform returns the whole message header unmodified including
+ *	 the token.
+ */
+struct scmi_msg_hdr {
+	u8 id;
+	u8 protocol_id;
+	u16 seq;
+	u32 status;
+	bool poll_completion;
+};
+
+/**
+ * struct scmi_msg - Message(Tx/Rx) structure
+ *
+ * @len: Length of data in the Buffer
+ * @buf: Buffer pointer
+ */
+struct scmi_msg {
+	u8 *buf;
+	size_t len;
+};
+
+/**
+ * struct scmi_xfer - Structure representing a message flow
+ *
+ * @hdr: Transmit message header
+ * @tx: Transmit message
+ * @rx: Receive message, the buffer should be pre-allocated to store
+ *	message. If request-ACK protocol is used, we can reuse the same
+ *	buffer for the rx path as we use for the tx path.
+ * @done: completion event
+ */
+
+struct scmi_xfer {
+	struct scmi_msg_hdr hdr;
+	struct scmi_msg tx;
+	struct scmi_msg rx;
+	struct completion done;
+};
+
+void scmi_put_one_xfer(struct scmi_handle *h, struct scmi_xfer *xfer);
+int scmi_do_xfer(struct scmi_handle *h, struct scmi_xfer *xfer);
+int scmi_one_xfer_init(struct scmi_handle *h, u8 msg_id, u8 msg_prot_id,
+		       size_t tx_size, size_t rx_size, struct scmi_xfer **p);
diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
new file mode 100644
index 000000000000..f01e0643ac7d
--- /dev/null
+++ b/drivers/firmware/arm_scmi/driver.c
@@ -0,0 +1,737 @@
+/*
+ * System Control and Management Interface (SCMI) Message Protocol driver
+ *
+ * SCMI Message Protocol is used between the System Control Processor(SCP)
+ * and the Application Processors(AP). The Message Handling Unit(MHU)
+ * provides a mechanism for inter-processor communication between SCP's
+ * Cortex M3 and AP.
+ *
+ * SCP offers control and management of the core/cluster power states,
+ * various power domain DVFS including the core/cluster, certain system
+ * clocks configuration, thermal sensors and many others.
+ *
+ * Copyright (C) 2017 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/bitmap.h>
+#include <linux/export.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/mailbox_client.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/semaphore.h>
+#include <linux/slab.h>
+
+#include "common.h"
+
+#define MSG_ID_SHIFT		0
+#define MSG_ID_MASK		0xff
+#define MSG_TYPE_SHIFT		8
+#define MSG_TYPE_MASK		0x3
+#define MSG_PROTOCOL_ID_SHIFT	10
+#define MSG_PROTOCOL_ID_MASK	0xff
+#define MSG_TOKEN_ID_SHIFT	18
+#define MSG_TOKEN_ID_MASK	0x3ff
+#define MSG_XTRACT_TOKEN(header)	\
+	(((header) >> MSG_TOKEN_ID_SHIFT) & MSG_TOKEN_ID_MASK)
+
+enum scmi_error_codes {
+	SCMI_SUCCESS = 0,	/* Success */
+	SCMI_ERR_SUPPORT = -1,	/* Not supported */
+	SCMI_ERR_PARAMS = -2,	/* Invalid Parameters */
+	SCMI_ERR_ACCESS = -3,	/* Invalid access/permission denied */
+	SCMI_ERR_ENTRY = -4,	/* Not found */
+	SCMI_ERR_RANGE = -5,	/* Value out of range */
+	SCMI_ERR_BUSY = -6,	/* Device busy */
+	SCMI_ERR_COMMS = -7,	/* Communication Error */
+	SCMI_ERR_GENERIC = -8,	/* Generic Error */
+	SCMI_ERR_HARDWARE = -9,	/* Hardware Error */
+	SCMI_ERR_PROTOCOL = -10,/* Protocol Error */
+	SCMI_ERR_MAX
+};
+
+/* List of all  SCMI devices active in system */
+static LIST_HEAD(scmi_list);
+/* Protection for the entire list */
+static DEFINE_MUTEX(scmi_list_mutex);
+
+/**
+ * struct scmi_xfers_info - Structure to manage transfer information
+ *
+ * @sem_xfer_count: Counting Semaphore for managing max simultaneous
+ *	Messages.
+ * @xfer_block: Preallocated Message array
+ * @xfer_alloc_table: Bitmap table for allocated messages.
+ *	Index of this bitmap table is also used for message
+ *	sequence identifier.
+ * @xfer_lock: Protection for message allocation
+ */
+struct scmi_xfers_info {
+	struct semaphore sem_xfer_count;
+	struct scmi_xfer *xfer_block;
+	unsigned long *xfer_alloc_table;
+	/* protect transfer allocation */
+	spinlock_t xfer_lock;
+};
+
+/**
+ * struct scmi_desc - Description of SoC integration
+ *
+ * @max_rx_timeout_ms: Timeout for communication with SoC (in Milliseconds)
+ * @max_msg: Maximum number of messages that can be pending
+ *	simultaneously in the system
+ * @max_msg_size: Maximum size of data per message that can be handled.
+ */
+struct scmi_desc {
+	int max_rx_timeout_ms;
+	int max_msg;
+	int max_msg_size;
+};
+
+/**
+ * struct scmi_info - Structure representing a  SCMI instance
+ *
+ * @dev: Device pointer
+ * @desc: SoC description for this instance
+ * @handle: Instance of SCMI handle to send to clients
+ * @cl: Mailbox Client
+ * @tx_chan: Transmit mailbox channel
+ * @rx_chan: Receive mailbox channel
+ * @tx_payload: Transmit mailbox channel payload area
+ * @rx_payload: Receive mailbox channel payload area
+ * @minfo: Message info
+ * @node: list head
+ * @users: Number of users of this instance
+ */
+struct scmi_info {
+	struct device *dev;
+	const struct scmi_desc *desc;
+	struct scmi_handle handle;
+	struct mbox_client cl;
+	struct mbox_chan *tx_chan;
+	struct mbox_chan *rx_chan;
+	void __iomem *tx_payload;
+	void __iomem *rx_payload;
+	struct scmi_xfers_info minfo;
+	struct list_head node;
+	int users;
+};
+
+#define client_to_scmi_info(c)	container_of(c, struct scmi_info, cl)
+#define handle_to_scmi_info(h)	container_of(h, struct scmi_info, handle)
+
+/*
+ * The SCP firmware only executes in little-endian mode, so any buffers
+ * shared through SCMI should have their contents converted to little-endian
+ */
+struct scmi_shared_mem {
+	__le32 reserved;
+	__le32 channel_status;
+#define SCMI_SHMEM_CHAN_STAT_CHANNEL_ERROR	BIT(1)
+#define SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE	BIT(0)
+	__le32 reserved1[2];
+	__le32 flags;
+#define SCMI_SHMEM_FLAG_INTR_ENABLED	BIT(0)
+	__le32 length;
+	__le32 msg_header;
+	u8 msg_payload[0];
+} __packed;
+
+static int scmi_linux_errmap[] = {
+	/* better than switch case as long as return value is continuous */
+	0,			/* SCMI_SUCCESS */
+	-EOPNOTSUPP,		/* SCMI_ERR_SUPPORT */
+	-EINVAL,		/* SCMI_ERR_PARAM */
+	-EACCES,		/* SCMI_ERR_ACCESS */
+	-ENOENT,		/* SCMI_ERR_ENTRY */
+	-ERANGE,		/* SCMI_ERR_RANGE */
+	-EBUSY,			/* SCMI_ERR_BUSY */
+	-ECOMM,			/* SCMI_ERR_COMMS */
+	-EIO,			/* SCMI_ERR_GENERIC */
+	-EREMOTEIO,		/* SCMI_ERR_HARDWARE */
+	-EPROTO,		/* SCMI_ERR_PROTOCOL */
+};
+
+static inline int scmi_to_linux_errno(int errno)
+{
+	if (errno < SCMI_SUCCESS && errno > SCMI_ERR_MAX)
+		return scmi_linux_errmap[-errno];
+	return -EIO;
+}
+
+/**
+ * scmi_dump_header_dbg() - Helper to dump a message header.
+ *
+ * @dev: Device pointer corresponding to the SCMI entity
+ * @hdr: pointer to header.
+ */
+static inline void scmi_dump_header_dbg(struct device *dev,
+					struct scmi_msg_hdr *hdr)
+{
+	dev_dbg(dev, "Command ID: %x Sequence ID: %x Protocol: %x\n",
+		hdr->id, hdr->seq, hdr->protocol_id);
+}
+
+/**
+ * scmi_rx_callback() - mailbox client callback for receive messages
+ *
+ * @cl: client pointer
+ * @m: mailbox message
+ *
+ * Processes one received message to appropriate transfer information and
+ * signals completion of the transfer.
+ *
+ * NOTE: This function will be invoked in IRQ context, hence should be
+ * as optimal as possible.
+ */
+static void scmi_rx_callback(struct mbox_client *cl, void *m)
+{
+	u16 xfer_id;
+	struct scmi_xfer *xfer;
+	struct scmi_info *info = client_to_scmi_info(cl);
+	struct scmi_xfers_info *minfo = &info->minfo;
+	struct device *dev = info->dev;
+	struct scmi_shared_mem *mem = info->tx_payload;
+
+	xfer_id = MSG_XTRACT_TOKEN(mem->msg_header);
+
+	/*
+	 * Are we even expecting this?
+	 */
+	if (!test_bit(xfer_id, minfo->xfer_alloc_table)) {
+		dev_err(dev, "message for %d is not expected!\n", xfer_id);
+		return;
+	}
+
+	xfer = &minfo->xfer_block[xfer_id];
+
+	scmi_dump_header_dbg(dev, &xfer->hdr);
+	/* Is the message of valid length? */
+	if (xfer->rx.len > info->desc->max_msg_size) {
+		dev_err(dev, "unable to handle %lu xfer(max %d)\n",
+			xfer->rx.len, info->desc->max_msg_size);
+		return;
+	}
+
+	xfer->hdr.status = le32_to_cpu(*(__le32 *)mem->msg_payload);
+	/* Skip the length of header and statues in payload area i.e 8 bytes*/
+	xfer->rx.len = min_t(size_t, xfer->rx.len, mem->length - 8);
+
+	/* Take a copy to the rx buffer.. */
+	memcpy_fromio(xfer->rx.buf, mem->msg_payload + 4, xfer->rx.len);
+	complete(&xfer->done);
+}
+
+/**
+ * pack_scmi_header() - packs and returns 32-bit header
+ *
+ * @hdr: pointer to header containing all the information on message id,
+ *	protocol id and sequence id.
+ */
+static inline u32 pack_scmi_header(struct scmi_msg_hdr *hdr)
+{
+	return ((hdr->id & MSG_ID_MASK) << MSG_ID_SHIFT) |
+	   ((hdr->seq & MSG_TOKEN_ID_MASK) << MSG_TOKEN_ID_SHIFT) |
+	   ((hdr->protocol_id & MSG_PROTOCOL_ID_MASK) << MSG_PROTOCOL_ID_SHIFT);
+}
+
+/**
+ * scmi_tx_prepare() - mailbox client callback to prepare for the transfer
+ *
+ * @cl: client pointer
+ * @m: mailbox message
+ *
+ * This function prepares the shared memory which contains the header and the
+ * payload.
+ */
+static void scmi_tx_prepare(struct mbox_client *cl, void *m)
+{
+	struct scmi_xfer *t = m;
+	struct scmi_info *info = client_to_scmi_info(cl);
+	struct scmi_shared_mem *mem = info->tx_payload;
+
+	mem->channel_status = 0x0; /* Mark channel busy + clear error */
+	mem->flags = t->hdr.poll_completion ? 0 : SCMI_SHMEM_FLAG_INTR_ENABLED;
+	mem->length = sizeof(mem->msg_header) + t->tx.len;
+	mem->msg_header = cpu_to_le32(pack_scmi_header(&t->hdr));
+	if (t->tx.buf)
+		memcpy_toio(mem->msg_payload, t->tx.buf, t->tx.len);
+}
+
+/**
+ * scmi_one_xfer_get() - Allocate one message
+ *
+ * @handle: SCMI entity handle
+ *
+ * Helper function which is used by various command functions that are
+ * exposed to clients of this driver for allocating a message traffic event.
+ *
+ * This function can sleep depending on pending requests already in the system
+ * for the SCMI entity. Further, this also holds a spinlock to maintain
+ * integrity of internal data structures.
+ *
+ * Return: 0 if all went fine, else corresponding error.
+ */
+static struct scmi_xfer *scmi_one_xfer_get(struct scmi_handle *handle)
+{
+	u16 xfer_id;
+	int ret, timeout;
+	struct scmi_xfer *xfer;
+	unsigned long flags, bit_pos;
+	struct scmi_info *info = handle_to_scmi_info(handle);
+	struct scmi_xfers_info *minfo = &info->minfo;
+
+	/*
+	 * Ensure we have only controlled number of pending messages.
+	 * Ideally, we might just have to wait a single message, be
+	 * conservative and wait 5 times that..
+	 */
+	timeout = msecs_to_jiffies(info->desc->max_rx_timeout_ms) * 5;
+	ret = down_timeout(&minfo->sem_xfer_count, timeout);
+	if (ret < 0)
+		return ERR_PTR(ret);
+
+	/* Keep the locked section as small as possible */
+	spin_lock_irqsave(&minfo->xfer_lock, flags);
+	bit_pos = find_first_zero_bit(minfo->xfer_alloc_table,
+				      info->desc->max_msg);
+	set_bit(bit_pos, minfo->xfer_alloc_table);
+	spin_unlock_irqrestore(&minfo->xfer_lock, flags);
+
+	xfer_id = bit_pos;
+
+	xfer = &minfo->xfer_block[xfer_id];
+	xfer->hdr.seq = xfer_id;
+	reinit_completion(&xfer->done);
+
+	return xfer;
+}
+
+/**
+ * scmi_put_one_xfer() - Release a message
+ *
+ * @minfo: transfer info pointer
+ * @xfer: message that was reserved by scmi_one_xfer_get
+ *
+ * This holds a spinlock to maintain integrity of internal data structures.
+ */
+void scmi_put_one_xfer(struct scmi_handle *handle, struct scmi_xfer *xfer)
+{
+	u16 xfer_id;
+	unsigned long flags;
+	struct scmi_msg_hdr *hdr;
+	struct scmi_info *info = handle_to_scmi_info(handle);
+	struct scmi_xfers_info *minfo = &info->minfo;
+
+	hdr = (struct scmi_msg_hdr *)xfer->tx.buf;
+	xfer_id = hdr->seq;
+
+	/*
+	 * Keep the locked section as small as possible
+	 * NOTE: we might escape with smp_mb and no lock here..
+	 * but just be conservative and symmetric.
+	 */
+	spin_lock_irqsave(&minfo->xfer_lock, flags);
+	clear_bit(xfer_id, minfo->xfer_alloc_table);
+	spin_unlock_irqrestore(&minfo->xfer_lock, flags);
+
+	/* Increment the count for the next user to get through */
+	up(&minfo->sem_xfer_count);
+}
+
+/**
+ * scmi_do_xfer() - Do one transfer
+ *
+ * @info: Pointer to SCMI entity information
+ * @xfer: Transfer to initiate and wait for response
+ *
+ * Return: -ETIMEDOUT in case of no response, if transmit error,
+ *   return corresponding error, else if all goes well,
+ *   return 0.
+ */
+int scmi_do_xfer(struct scmi_handle *handle, struct scmi_xfer *xfer)
+{
+	int ret;
+	int timeout;
+	struct scmi_info *info = handle_to_scmi_info(handle);
+	struct device *dev = info->dev;
+
+	ret = mbox_send_message(info->tx_chan, xfer);
+	if (ret < 0) {
+		dev_dbg(dev, "mbox send fail %d\n", ret);
+		return ret;
+	}
+
+	/* mbox_send_message returns non-negative value on success, so reset */
+	ret = 0;
+
+	/* And we wait for the response. */
+	timeout = msecs_to_jiffies(info->desc->max_rx_timeout_ms);
+	if (!wait_for_completion_timeout(&xfer->done, timeout)) {
+		dev_err(dev, "mbox timed out in resp(caller: %pF)\n",
+			(void *)_RET_IP_);
+		ret = -ETIMEDOUT;
+	} else if (xfer->hdr.status) {
+		ret = scmi_to_linux_errno(xfer->hdr.status);
+	}
+	/*
+	 * NOTE: we might prefer not to need the mailbox ticker to manage the
+	 * transfer queueing since the protocol layer queues things by itself.
+	 * Unfortunately, we have to kick the mailbox framework after we have
+	 * received our message.
+	 */
+	mbox_client_txdone(info->tx_chan, ret);
+
+	return ret;
+}
+
+/**
+ * scmi_one_xfer_init() - Allocate and initialise one message
+ *
+ * @handle: SCMI entity handle
+ * @msg_id: Message identifier
+ * @msg_prot_id: Protocol identifier for the message
+ * @tx_size: transmit message size
+ * @rx_size: receive message size
+ * @p: pointer to the allocated and initialised message
+ *
+ * This function allocates the message using @scmi_one_xfer_get and
+ * initialise the header.
+ *
+ * Return: 0 if all went fine with @p pointing to message, else
+ *	corresponding error.
+ */
+int scmi_one_xfer_init(struct scmi_handle *handle, u8 msg_id, u8 msg_prot_id,
+		       size_t tx_size, size_t rx_size, struct scmi_xfer **p)
+{
+	int ret;
+	struct scmi_xfer *xfer;
+	struct scmi_info *info = handle_to_scmi_info(handle);
+	struct device *dev = info->dev;
+
+	/* Ensure we have sane transfer sizes */
+	if (rx_size > info->desc->max_msg_size ||
+	    tx_size > info->desc->max_msg_size)
+		return -ERANGE;
+
+	xfer = scmi_one_xfer_get(handle);
+	if (IS_ERR(xfer)) {
+		ret = PTR_ERR(xfer);
+		dev_err(dev, "failed to get free message slot(%d)\n", ret);
+		return ret;
+	}
+
+	xfer->tx.len = tx_size;
+	xfer->rx.len = rx_size ? : info->desc->max_msg_size;
+	xfer->hdr.id = msg_id;
+	xfer->hdr.protocol_id = msg_prot_id;
+
+	*p = xfer;
+	return 0;
+}
+
+/**
+ * scmi_handle_get() - Get the  SCMI handle for a device
+ *
+ * @dev: pointer to device for which we want SCMI handle
+ *
+ * NOTE: The function does not track individual clients of the framework
+ * and is expected to be maintained by caller of  SCMI protocol library.
+ * scmi_put_handle must be balanced with successful scmi_handle_get
+ *
+ * Return: pointer to handle if successful, else:
+ *	-EPROBE_DEFER if the instance is not ready
+ *	-ENODEV if the required node handler is missing
+ *	-EINVAL if invalid conditions are encountered.
+ */
+const struct scmi_handle *scmi_handle_get(struct device *dev)
+{
+	struct list_head *p;
+	struct scmi_info *info;
+	struct device_node *scmi_np;
+	struct scmi_handle *handle = NULL;
+
+	if (!dev) {
+		pr_err("missing device pointer\n");
+		return ERR_PTR(-EINVAL);
+	}
+	scmi_np = of_get_parent(dev->of_node);
+	if (!scmi_np) {
+		dev_err(dev, "no OF information\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	mutex_lock(&scmi_list_mutex);
+	list_for_each(p, &scmi_list) {
+		info = list_entry(p, struct scmi_info, node);
+		if (scmi_np == info->dev->of_node) {
+			handle = &info->handle;
+			info->users++;
+			break;
+		}
+	}
+	mutex_unlock(&scmi_list_mutex);
+	of_node_put(scmi_np);
+
+	if (!handle)
+		return ERR_PTR(-EPROBE_DEFER);
+
+	return handle;
+}
+EXPORT_SYMBOL_GPL(scmi_handle_get);
+
+/**
+ * scmi_put_handle() - Release the handle acquired by scmi_handle_get
+ *
+ * @handle: handle acquired by scmi_handle_get
+ *
+ * NOTE: The function does not track individual clients of the framework
+ * and is expected to be maintained by caller of  SCMI protocol library.
+ * scmi_put_handle must be balanced with successful scmi_handle_get
+ *
+ * Return: 0 is successfully released
+ *	if an error pointer was passed, it returns the error value back,
+ *	if null was passed, it returns -EINVAL;
+ */
+int scmi_put_handle(const struct scmi_handle *handle)
+{
+	struct scmi_info *info;
+
+	if (IS_ERR(handle))
+		return PTR_ERR(handle);
+	if (!handle)
+		return -EINVAL;
+
+	info = handle_to_scmi_info(handle);
+	mutex_lock(&scmi_list_mutex);
+	if (!WARN_ON(!info->users))
+		info->users--;
+	mutex_unlock(&scmi_list_mutex);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(scmi_put_handle);
+
+static void devm_scmi_release(struct device *dev, void *res)
+{
+	const struct scmi_handle **ptr = res;
+	const struct scmi_handle *handle = *ptr;
+	int ret;
+
+	ret = scmi_put_handle(handle);
+	if (ret)
+		dev_err(dev, "failed to put handle %d\n", ret);
+}
+
+/**
+ * devm_scmi_handle_get() - Managed get handle
+ * @dev: device for which we want SCMI handle for.
+ *
+ * NOTE: This releases the handle once the device resources are
+ * no longer needed. MUST NOT BE released with scmi_put_handle.
+ * The function does not track individual clients of the framework
+ * and is expected to be maintained by caller of  SCMI protocol library.
+ *
+ * Return: 0 if all went fine, else corresponding error.
+ */
+const struct scmi_handle *devm_scmi_handle_get(struct device *dev)
+{
+	const struct scmi_handle **ptr;
+	const struct scmi_handle *handle;
+
+	ptr = devres_alloc(devm_scmi_release, sizeof(*ptr), GFP_KERNEL);
+	if (!ptr)
+		return ERR_PTR(-ENOMEM);
+	handle = scmi_handle_get(dev);
+
+	if (!IS_ERR(handle)) {
+		*ptr = handle;
+		devres_add(dev, ptr);
+	} else {
+		devres_free(ptr);
+	}
+
+	return handle;
+}
+EXPORT_SYMBOL_GPL(devm_scmi_handle_get);
+
+static const struct scmi_desc scmi_generic_desc = {
+	.max_rx_timeout_ms = 30,	/* we may increase this if required */
+	.max_msg = 20,		/* Limited by MBOX_TX_QUEUE_LEN */
+	.max_msg_size = 128,
+};
+
+/* Each compatible listed below must have descriptor associated with it */
+static const struct of_device_id scmi_of_match[] = {
+	{ .compatible = "arm,scmi", .data = &scmi_generic_desc },
+	{ /* Sentinel */ },
+};
+
+MODULE_DEVICE_TABLE(of, scmi_of_match);
+
+static int scmi_xfer_info_init(struct scmi_info *sinfo)
+{
+	int i;
+	struct scmi_xfer *xfer;
+	struct device *dev = sinfo->dev;
+	const struct scmi_desc *desc = sinfo->desc;
+	struct scmi_xfers_info *info = &sinfo->minfo;
+
+	/* Pre-allocated messages, no more than what hdr.seq can support */
+	if (WARN_ON(desc->max_msg >= (MSG_TOKEN_ID_MASK + 1))) {
+		dev_err(dev, "Maximum message of %d exceeds supported %d\n",
+			desc->max_msg, MSG_TOKEN_ID_MASK + 1);
+		return -EINVAL;
+	}
+
+	info->xfer_block = devm_kcalloc(dev, desc->max_msg,
+					sizeof(*info->xfer_block), GFP_KERNEL);
+	if (!info->xfer_block)
+		return -ENOMEM;
+
+	info->xfer_alloc_table = devm_kcalloc(dev, BITS_TO_LONGS(desc->max_msg),
+					      sizeof(long), GFP_KERNEL);
+	if (!info->xfer_alloc_table)
+		return -ENOMEM;
+
+	bitmap_zero(info->xfer_alloc_table, desc->max_msg);
+
+	/* Pre-initialize the buffer pointer to pre-allocated buffers */
+	for (i = 0, xfer = info->xfer_block; i < desc->max_msg; i++, xfer++) {
+		xfer->rx.buf = devm_kcalloc(dev, sizeof(*xfer->rx.buf),
+					    desc->max_msg_size, GFP_KERNEL);
+		if (!xfer->rx.buf)
+			return -ENOMEM;
+
+		xfer->tx.buf = xfer->rx.buf;
+		init_completion(&xfer->done);
+	}
+
+	spin_lock_init(&info->xfer_lock);
+
+	sema_init(&info->sem_xfer_count, desc->max_msg);
+
+	return 0;
+}
+
+static int scmi_probe(struct platform_device *pdev)
+{
+	int ret = -EINVAL;
+	struct resource res;
+	resource_size_t size;
+	struct mbox_client *cl;
+	struct scmi_handle *handle;
+	const struct scmi_desc *desc;
+	struct scmi_info *info = NULL;
+	struct device *dev = &pdev->dev;
+	struct device_node *shmem, *np = dev->of_node;
+
+	desc = of_match_device(scmi_of_match, dev)->data;
+
+	info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
+	if (!info)
+		return -ENOMEM;
+
+	info->dev = dev;
+	info->desc = desc;
+	INIT_LIST_HEAD(&info->node);
+
+	ret = scmi_xfer_info_init(info);
+	if (ret)
+		return ret;
+
+	platform_set_drvdata(pdev, info);
+
+	cl = &info->cl;
+	cl->dev = dev;
+	cl->rx_callback = scmi_rx_callback;
+	cl->tx_prepare = scmi_tx_prepare;
+	cl->tx_block = false;
+	cl->knows_txdone = true;
+
+	shmem = of_parse_phandle(np, "shmem", 0);
+	ret = of_address_to_resource(shmem, 0, &res);
+	of_node_put(shmem);
+	if (ret) {
+		dev_err(dev, "failed to get SCMI Tx payload mem resource\n");
+		return ret;
+	}
+
+	size = resource_size(&res);
+	info->tx_payload = devm_ioremap(dev, res.start, size);
+	if (!info->tx_payload) {
+		dev_err(dev, "failed to ioremap SCMI Tx payload\n");
+		ret = -EADDRNOTAVAIL;
+		return ret;
+	}
+
+	info->tx_chan = mbox_request_channel_byname(cl, "tx");
+	if (IS_ERR(info->tx_chan)) {
+		ret = PTR_ERR(info->tx_chan);
+		goto out;
+	}
+
+	handle = &info->handle;
+	handle->dev = info->dev;
+
+	mutex_lock(&scmi_list_mutex);
+	list_add_tail(&info->node, &scmi_list);
+	mutex_unlock(&scmi_list_mutex);
+
+	return of_platform_populate(dev->of_node, NULL, NULL, dev);
+out:
+	if (!IS_ERR(info->tx_chan))
+		mbox_free_channel(info->tx_chan);
+	return ret;
+}
+
+static int scmi_remove(struct platform_device *pdev)
+{
+	int ret = 0;
+	struct scmi_info *info = platform_get_drvdata(pdev);
+
+	of_platform_depopulate(&pdev->dev);
+
+	mutex_lock(&scmi_list_mutex);
+	if (info->users)
+		ret = -EBUSY;
+	else
+		list_del(&info->node);
+	mutex_unlock(&scmi_list_mutex);
+
+	if (!ret)
+		/* Safe to free channels since no more users */
+		mbox_free_channel(info->tx_chan);
+
+	return ret;
+}
+
+static struct platform_driver scmi_driver = {
+	.driver = {
+		   .name = "arm-scmi",
+		   .of_match_table = of_match_ptr(scmi_of_match),
+		   },
+	.probe = scmi_probe,
+	.remove = scmi_remove,
+};
+
+module_platform_driver(scmi_driver);
+
+MODULE_ALIAS("platform: arm-scmi");
+MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");
+MODULE_DESCRIPTION("ARM SCMI protocol driver");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h
new file mode 100644
index 000000000000..0c795a765110
--- /dev/null
+++ b/include/linux/scmi_protocol.h
@@ -0,0 +1,48 @@
+/*
+ * SCMI Message Protocol driver header
+ *
+ * Copyright (C) 2017 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/types.h>
+
+/**
+ * struct scmi_handle - Handle returned to ARM SCMI clients for usage.
+ *
+ * @dev: pointer to the SCMI device
+ */
+struct scmi_handle {
+	struct device *dev;
+};
+
+#if IS_REACHABLE(CONFIG_ARM_SCMI_PROTOCOL)
+int scmi_put_handle(const struct scmi_handle *handle);
+const struct scmi_handle *scmi_handle_get(struct device *dev);
+const struct scmi_handle *devm_scmi_handle_get(struct device *dev);
+#else
+static inline int scmi_put_handle(const struct scmi_handle *handle)
+{
+	return 0;
+}
+
+static inline const struct scmi_handle *scmi_handle_get(struct device *dev)
+{
+	return NULL;
+}
+
+static inline const struct scmi_handle *devm_scmi_handle_get(struct device *dev)
+{
+	return NULL;
+}
+#endif /* CONFIG_ARM_SCMI_PROTOCOL */
-- 
2.7.4

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

* [RFC PATCH 3/8] firmware: arm_scmi: add common infrastructure and support for base protocol
  2017-06-07 16:10 [RFC PATCH 0/8] firmware: ARM System Control and Management Interface(SCMI) support Sudeep Holla
  2017-06-07 16:10 ` [RFC PATCH 1/8] Documentation: add DT binding for ARM System Control and Management Interface(SCMI) protocol Sudeep Holla
  2017-06-07 16:10 ` [RFC PATCH 2/8] firmware: arm_scmi: add basic driver infrastructure for SCMI Sudeep Holla
@ 2017-06-07 16:10 ` Sudeep Holla
  2017-06-07 19:19   ` Roy Franz
  2017-06-07 16:10 ` [RFC PATCH 4/8] firmware: arm_scmi: add initial support for performance protocol Sudeep Holla
                   ` (4 subsequent siblings)
  7 siblings, 1 reply; 23+ messages in thread
From: Sudeep Holla @ 2017-06-07 16:10 UTC (permalink / raw)
  To: linux-kernel, devicetree
  Cc: Sudeep Holla, Roy Franz, Harb Abdulhamid, Nishanth Menon, Arnd Bergmann

The base protocol describes the properties of the implementation and
provide generic error management. The base protocol provides commands
to describe protocol version, discover implementation specific
attributes and vendor/sub-vendor identification, list of protocols
implemented and the various agents are in the system including OSPM
and the platform. It also supports registering for notifications of
platform errors.

This protocol is mandatory. This patch adds support for the same along
with some basic infrastructure to add support for other protocols.

Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
---
 drivers/firmware/arm_scmi/Makefile |   2 +-
 drivers/firmware/arm_scmi/base.c   | 290 +++++++++++++++++++++++++++++++++++++
 drivers/firmware/arm_scmi/common.h |  46 ++++++
 drivers/firmware/arm_scmi/driver.c |  67 +++++++++
 include/linux/scmi_protocol.h      |  28 ++++
 5 files changed, 432 insertions(+), 1 deletion(-)
 create mode 100644 drivers/firmware/arm_scmi/base.c

diff --git a/drivers/firmware/arm_scmi/Makefile b/drivers/firmware/arm_scmi/Makefile
index 58e94c95e523..21d01d1d6b9c 100644
--- a/drivers/firmware/arm_scmi/Makefile
+++ b/drivers/firmware/arm_scmi/Makefile
@@ -1,2 +1,2 @@
 obj-$(CONFIG_ARM_SCMI_PROTOCOL)	= arm_scmi.o
-arm_scmi-y = driver.o
+arm_scmi-y = base.o driver.o
diff --git a/drivers/firmware/arm_scmi/base.c b/drivers/firmware/arm_scmi/base.c
new file mode 100644
index 000000000000..1191a409ea73
--- /dev/null
+++ b/drivers/firmware/arm_scmi/base.c
@@ -0,0 +1,290 @@
+/*
+ * System Control and Management Interface (SCMI) Base Protocol
+ *
+ * Copyright (C) 2017 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "common.h"
+
+enum scmi_base_protocol_cmd {
+	BASE_DISCOVER_VENDOR = 0x3,
+	BASE_DISCOVER_SUB_VENDOR = 0x4,
+	BASE_DISCOVER_IMPLEMENT_VERSION = 0x5,
+	BASE_DISCOVER_LIST_PROTOCOLS = 0x6,
+	BASE_DISCOVER_AGENT = 0x7,
+	BASE_NOTIFY_ERRORS = 0x8,
+};
+
+struct scmi_msg_resp_base_attributes {
+	    u8 num_protocols;
+	    u8 num_agents;
+	__le16 reserved;
+} __packed;
+
+/**
+ * scmi_base_attributes_get() - gets the implementation details
+ *	that are associated with the base protocol.
+ *
+ * @handle - SCMI entity handle
+ *
+ * Return: 0 on success, else appropriate SCMI error.
+ */
+static int scmi_base_attributes_get(struct scmi_handle *handle)
+{
+	int ret;
+	struct scmi_xfer *t;
+	struct scmi_msg_resp_base_attributes *attr_info;
+	struct scmi_revision_info *rev = handle->version;
+
+	ret = scmi_one_xfer_init(handle, PROTOCOL_ATTRIBUTES,
+				 SCMI_PROTOCOL_BASE, 0, sizeof(*attr_info), &t);
+	if (ret)
+		return ret;
+
+	ret = scmi_do_xfer(handle, t);
+	if (!ret) {
+		attr_info = (struct scmi_msg_resp_base_attributes *)t->rx.buf;
+		rev->num_protocols = attr_info->num_protocols;
+		rev->num_agents = attr_info->num_agents;
+	}
+
+	scmi_put_one_xfer(handle, t);
+	return ret;
+}
+
+/**
+ * scmi_base_vendor_id_get() - gets vendor/subvendor identifier ASCII string.
+ *
+ * @handle - SCMI entity handle
+ * @sub_vendor - specify true if sub-vendor ID is needed
+ *
+ * Return: 0 on success, else appropriate SCMI error.
+ */
+static int scmi_base_vendor_id_get(struct scmi_handle *handle, bool sub_vendor)
+{
+	u8 cmd;
+	int ret, size;
+	char *vendor_id;
+	struct scmi_xfer *t;
+	struct scmi_revision_info *rev = handle->version;
+
+	if (sub_vendor) {
+		cmd = BASE_DISCOVER_SUB_VENDOR;
+		vendor_id = rev->sub_vendor_id;
+		size = ARRAY_SIZE(rev->sub_vendor_id);
+	} else {
+		cmd = BASE_DISCOVER_VENDOR;
+		vendor_id = rev->vendor_id;
+		size = ARRAY_SIZE(rev->vendor_id);
+	}
+
+	ret = scmi_one_xfer_init(handle, cmd, SCMI_PROTOCOL_BASE, 0, size, &t);
+	if (ret)
+		return ret;
+
+	ret = scmi_do_xfer(handle, t);
+	if (!ret)
+		memcpy(vendor_id, t->rx.buf, size);
+
+	scmi_put_one_xfer(handle, t);
+	return ret;
+}
+
+/**
+ * scmi_base_implementation_version_get() - gets a vendor-specific
+ *	implementation 32-bit version. The format of the version number is
+ *	vendor-specific
+ *
+ * @handle - SCMI entity handle
+ *
+ * Return: 0 on success, else appropriate SCMI error.
+ */
+static int scmi_base_implementation_version_get(struct scmi_handle *handle)
+{
+	int ret;
+	u32 *impl_ver;
+	struct scmi_xfer *t;
+	struct scmi_revision_info *rev = handle->version;
+
+	ret = scmi_one_xfer_init(handle, BASE_DISCOVER_IMPLEMENT_VERSION,
+				 SCMI_PROTOCOL_BASE, 0, sizeof(*impl_ver), &t);
+	if (ret)
+		return ret;
+
+	ret = scmi_do_xfer(handle, t);
+	if (ret) {
+		impl_ver = (u32 *)t->rx.buf;
+		rev->impl_ver = le32_to_cpu(*impl_ver);
+	}
+
+	scmi_put_one_xfer(handle, t);
+	return ret;
+}
+
+/**
+ * scmi_base_implementation_list_get() - gets the list of protocols it is
+ *	OSPM is allowed to access
+ *
+ * @handle - SCMI entity handle
+ * @protocols_imp - pointer to hold the list of protocol identifiers
+ *
+ * Return: 0 on success, else appropriate SCMI error.
+ */
+static int scmi_base_implementation_list_get(struct scmi_handle *handle,
+					     u8 *protocols_imp)
+{
+	u8 *list;
+	int ret, loop;
+	struct scmi_xfer *t;
+	__le32 *num_skip, *num_ret;
+	u32 tot_num_ret = 0, loop_num_ret;
+	struct device *dev = handle->dev;
+
+	ret = scmi_one_xfer_init(handle, BASE_DISCOVER_LIST_PROTOCOLS,
+				 SCMI_PROTOCOL_BASE, sizeof(*num_skip), 0, &t);
+	if (ret)
+		return ret;
+
+	num_skip = (__le32 *)t->tx.buf;
+	num_ret = (__le32 *)t->rx.buf;
+	list = t->rx.buf + sizeof(*num_ret);
+
+	do {
+		/* Set the number of protocols to be skipped/already read */
+		*num_skip = cpu_to_le32(tot_num_ret);
+
+		ret = scmi_do_xfer(handle, t);
+		if (ret)
+			break;
+
+		loop_num_ret = le32_to_cpu(*num_ret);
+		if (tot_num_ret + loop_num_ret > MAX_PROTOCOLS_IMP) {
+			dev_err(dev, "No. of Protocol > MAX_PROTOCOLS_IMP");
+			break;
+		}
+
+		for (loop = 0; loop < loop_num_ret; loop++)
+			protocols_imp[tot_num_ret + loop] = *(list + loop);
+
+		tot_num_ret += loop_num_ret;
+	} while (loop_num_ret);
+
+	scmi_put_one_xfer(handle, t);
+	return ret;
+}
+
+/**
+ * scmi_base_discover_agent_get() - discover the name of an agent
+ *
+ * @handle - SCMI entity handle
+ * @id - Agent identifier
+ * @name - Agent identifier ASCII string
+ *
+ * An agent id of 0 is reserved to identify the platform itself.
+ * Generally operating system is represented as "OSPM"
+ *
+ * Return: 0 on success, else appropriate SCMI error.
+ */
+static int
+scmi_base_discover_agent_get(struct scmi_handle *handle, int id, char *name)
+{
+	int ret;
+	struct scmi_xfer *t;
+
+	ret = scmi_one_xfer_init(handle, BASE_DISCOVER_AGENT,
+				 SCMI_PROTOCOL_BASE, sizeof(__le32),
+				 SCMI_MAX_STR_SIZE, &t);
+	if (ret)
+		return ret;
+
+	*(__le32 *)t->tx.buf = cpu_to_le32(id);
+
+	ret = scmi_do_xfer(handle, t);
+	if (!ret)
+		memcpy(name, t->rx.buf, SCMI_MAX_STR_SIZE);
+
+	scmi_put_one_xfer(handle, t);
+	return ret;
+}
+
+/**
+ * scmi_base_error_notifications_enable() - register/unregister for
+ *	notifications of errors in the platform
+ *
+ * @handle - SCMI entity handle
+ * @enable - Enable/Disable the notification
+ *
+ * Return: 0 on success, else appropriate SCMI error.
+ */
+static int scmi_base_error_notifications_enable(struct scmi_handle *handle,
+						bool enable)
+{
+	int ret;
+	struct scmi_xfer *t;
+
+	ret = scmi_one_xfer_init(handle, BASE_NOTIFY_ERRORS, SCMI_PROTOCOL_BASE,
+				 sizeof(__le32), 0, &t);
+	if (ret)
+		return ret;
+
+	*(__le32 *)t->tx.buf = cpu_to_le32(enable & BIT(0));
+
+	ret = scmi_do_xfer(handle, t);
+
+	scmi_put_one_xfer(handle, t);
+	return ret;
+}
+
+int scmi_base_protocol_init(struct scmi_handle *handle)
+{
+	int id, ret;
+	u8 *prot_imp;
+	u32 version;
+	char name[SCMI_MAX_STR_SIZE];
+	struct device *dev = handle->dev;
+	struct scmi_revision_info *rev = handle->version;
+
+	ret = scmi_version_get(handle, SCMI_PROTOCOL_BASE, &version);
+	if (ret)
+		return ret;
+
+	prot_imp = devm_kcalloc(dev, MAX_PROTOCOLS_IMP, sizeof(u8), GFP_KERNEL);
+	if (!prot_imp)
+		return -ENOMEM;
+
+	rev->major_ver = PROTOCOL_REV_MAJOR(version),
+	rev->minor_ver = PROTOCOL_REV_MINOR(version);
+
+	scmi_base_attributes_get(handle);
+	scmi_base_vendor_id_get(handle, false);
+	scmi_base_vendor_id_get(handle, true);
+	scmi_base_implementation_version_get(handle);
+	scmi_base_implementation_list_get(handle, prot_imp);
+	scmi_base_error_notifications_enable(handle, true);
+	scmi_setup_protocol_implemented(handle, prot_imp);
+
+	dev_info(dev, "SCMI Protocol %d.%d '%s:%s' Firmware Version 0x%x\n",
+		 rev->major_ver, rev->minor_ver, rev->vendor_id,
+		 rev->sub_vendor_id, rev->impl_ver);
+	dev_dbg(dev, "Found %d protocol(s) %d agent(s)\n", rev->num_protocols,
+		rev->num_agents);
+
+	for (id = 0; id < rev->num_agents; id++) {
+		scmi_base_discover_agent_get(handle, id, name);
+		dev_dbg(dev, "Agent %d: %s\n", id, name);
+	}
+
+	return 0;
+}
diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h
index a3038efa3a8d..24bc51dcc6c5 100644
--- a/drivers/firmware/arm_scmi/common.h
+++ b/drivers/firmware/arm_scmi/common.h
@@ -19,9 +19,50 @@
  */
 
 #include <linux/completion.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
 #include <linux/scmi_protocol.h>
 #include <linux/types.h>
 
+#define PROTOCOL_REV_MINOR_BITS	16
+#define PROTOCOL_REV_MINOR_MASK	((1U << PROTOCOL_REV_MINOR_BITS) - 1)
+#define PROTOCOL_REV_MAJOR(x)	((x) >> PROTOCOL_REV_MINOR_BITS)
+#define PROTOCOL_REV_MINOR(x)	((x) & PROTOCOL_REV_MINOR_MASK)
+#define MAX_PROTOCOLS_IMP	16
+
+enum scmi_std_protocol {
+	SCMI_PROTOCOL_BASE = 0x10,
+	SCMI_PROTOCOL_POWER = 0x11,
+	SCMI_PROTOCOL_SYSTEM = 0x12,
+	SCMI_PROTOCOL_PERF = 0x13,
+	SCMI_PROTOCOL_CLOCK = 0x14,
+	SCMI_PROTOCOL_SENSOR = 0x15,
+};
+
+enum scmi_common_cmd {
+	PROTOCOL_VERSION = 0x0,
+	PROTOCOL_ATTRIBUTES = 0x1,
+	PROTOCOL_MESSAGE_ATTRIBUTES = 0x2,
+};
+
+/**
+ * struct scmi_msg_resp_prot_version - Response for a message
+ *
+ * @major_version: Major version of the ABI that firmware supports
+ * @minor_version: Minor version of the ABI that firmware supports
+ *
+ * In general, ABI version changes follow the rule that minor version increments
+ * are backward compatible. Major revision changes in ABI may not be
+ * backward compatible.
+ *
+ * Response to a generic message with message type SCMI_MSG_VERSION
+ */
+struct scmi_msg_resp_prot_version {
+	__le16 minor_version;
+	__le16 major_version;
+} __packed;
+
 /**
  * struct scmi_msg_hdr - Message(Tx/Rx) header
  *
@@ -72,3 +113,8 @@ void scmi_put_one_xfer(struct scmi_handle *h, struct scmi_xfer *xfer);
 int scmi_do_xfer(struct scmi_handle *h, struct scmi_xfer *xfer);
 int scmi_one_xfer_init(struct scmi_handle *h, u8 msg_id, u8 msg_prot_id,
 		       size_t tx_size, size_t rx_size, struct scmi_xfer **p);
+int scmi_version_get(struct scmi_handle *h, u8 protocol, u32 *version);
+bool scmi_is_protocol_implemented(struct scmi_handle *h, u8 prot_id);
+void scmi_setup_protocol_implemented(struct scmi_handle *handle, u8 *prot_imp);
+
+int scmi_base_protocol_init(struct scmi_handle *h);
diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
index f01e0643ac7d..7b653c932edc 100644
--- a/drivers/firmware/arm_scmi/driver.c
+++ b/drivers/firmware/arm_scmi/driver.c
@@ -108,18 +108,22 @@ struct scmi_desc {
  * @dev: Device pointer
  * @desc: SoC description for this instance
  * @handle: Instance of SCMI handle to send to clients
+ * @version: SCMI revision information containing protocol version,
+ *	implementation version and (sub-)vendor identification.
  * @cl: Mailbox Client
  * @tx_chan: Transmit mailbox channel
  * @rx_chan: Receive mailbox channel
  * @tx_payload: Transmit mailbox channel payload area
  * @rx_payload: Receive mailbox channel payload area
  * @minfo: Message info
+ * @protocols_imp: list of protocols implemented
  * @node: list head
  * @users: Number of users of this instance
  */
 struct scmi_info {
 	struct device *dev;
 	const struct scmi_desc *desc;
+	struct scmi_revision_info version;
 	struct scmi_handle handle;
 	struct mbox_client cl;
 	struct mbox_chan *tx_chan;
@@ -127,6 +131,7 @@ struct scmi_info {
 	void __iomem *tx_payload;
 	void __iomem *rx_payload;
 	struct scmi_xfers_info minfo;
+	u8 *protocols_imp;
 	struct list_head node;
 	int users;
 };
@@ -445,6 +450,57 @@ int scmi_one_xfer_init(struct scmi_handle *handle, u8 msg_id, u8 msg_prot_id,
 }
 
 /**
+ * scmi_version_get() - command to get the revision of the SCMI entity
+ *
+ * @handle: Handle to SCMI entity information
+ *
+ * Updates the SCMI information in the internal data structure.
+ *
+ * Return: 0 if all went fine, else return appropriate error.
+ */
+int scmi_version_get(struct scmi_handle *handle, u8 protocol, u32 *version)
+{
+	int ret;
+	__le32 *rev_info;
+	struct scmi_xfer *t;
+
+	ret = scmi_one_xfer_init(handle, PROTOCOL_VERSION, protocol, 0,
+				 sizeof(*version), &t);
+	if (ret)
+		return ret;
+
+	rev_info = (__le32 *)t->rx.buf;
+
+	ret = scmi_do_xfer(handle, t);
+	if (!ret)
+		*version = le32_to_cpu(*rev_info);
+
+	scmi_put_one_xfer(handle, t);
+	return ret;
+}
+
+void scmi_setup_protocol_implemented(struct scmi_handle *handle, u8 *prot_imp)
+{
+	struct scmi_info *info = handle_to_scmi_info(handle);
+
+	info->protocols_imp = prot_imp;
+}
+
+bool scmi_is_protocol_implemented(struct scmi_handle *handle, u8 prot_id)
+{
+	int i;
+	struct scmi_info *info = handle_to_scmi_info(handle);
+
+	if (!info->protocols_imp)
+		return false;
+
+	for (i = 0; i < MAX_PROTOCOLS_IMP; i++)
+		if (info->protocols_imp[i] == prot_id)
+			return true;
+	return false;
+}
+
+/**
  * scmi_handle_get() - Get the  SCMI handle for a device
  *
  * @dev: pointer to device for which we want SCMI handle
@@ -642,6 +698,11 @@ static int scmi_probe(struct platform_device *pdev)
 
 	desc = of_match_device(scmi_of_match, dev)->data;
 
+	if (of_property_match_string(np, "method", "mailbox-doorbell") < 0) {
+		dev_err(dev, "invalid method property in %s\n", np->full_name);
+		return -EINVAL;
+	}
+
 	info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
 	if (!info)
 		return -ENOMEM;
@@ -687,6 +748,12 @@ static int scmi_probe(struct platform_device *pdev)
 
 	handle = &info->handle;
 	handle->dev = info->dev;
+	handle->version = &info->version;
+	ret = scmi_base_protocol_init(handle);
+	if (ret) {
+		dev_err(dev, "unable to communicate with SCMI(%d)\n", ret);
+		goto out;
+	}
 
 	mutex_lock(&scmi_list_mutex);
 	list_add_tail(&info->node, &scmi_list);
diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h
index 0c795a765110..901976fe211f 100644
--- a/include/linux/scmi_protocol.h
+++ b/include/linux/scmi_protocol.h
@@ -17,13 +17,41 @@
  */
 #include <linux/types.h>
 
+#define SCMI_MAX_STR_SIZE		16
+
+/**
+ * struct scmi_revision_info - version information structure
+ *
+ * @major_ver: Major ABI version. Change here implies risk of backward
+ *	compatibility break.
+ * @minor_ver: Minor ABI version. Change here implies new feature addition,
+ *	or compatible change in ABI.
+ * @num_protocols: Number of protocols that are implemented, excluding the
+ *	base protocol.
+ * @num_agents: Number of agents in the system.
+ * @impl_ver: A vendor-specific implementation version.
+ * @vendor_id: A vendor identifier(Null terminated ASCII string)
+ * @sub_vendor_id: A sub-vendor identifier(Null terminated ASCII string)
+ */
+struct scmi_revision_info {
+	u16 major_ver;
+	u16 minor_ver;
+	u8 num_protocols;
+	u8 num_agents;
+	u32 impl_ver;
+	char vendor_id[SCMI_MAX_STR_SIZE];
+	char sub_vendor_id[SCMI_MAX_STR_SIZE];
+};
+
 /**
  * struct scmi_handle - Handle returned to ARM SCMI clients for usage.
  *
  * @dev: pointer to the SCMI device
+ * @version: pointer to the structure containing SCMI version information
  */
 struct scmi_handle {
 	struct device *dev;
+	struct scmi_revision_info *version;
 };
 
 #if IS_REACHABLE(CONFIG_ARM_SCMI_PROTOCOL)
-- 
2.7.4

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

* [RFC PATCH 4/8] firmware: arm_scmi: add initial support for performance protocol
  2017-06-07 16:10 [RFC PATCH 0/8] firmware: ARM System Control and Management Interface(SCMI) support Sudeep Holla
                   ` (2 preceding siblings ...)
  2017-06-07 16:10 ` [RFC PATCH 3/8] firmware: arm_scmi: add common infrastructure and support for base protocol Sudeep Holla
@ 2017-06-07 16:10 ` Sudeep Holla
  2017-06-07 16:10 ` [RFC PATCH 5/8] firmware: arm_scmi: add initial support for clock protocol Sudeep Holla
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 23+ messages in thread
From: Sudeep Holla @ 2017-06-07 16:10 UTC (permalink / raw)
  To: linux-kernel, devicetree
  Cc: Sudeep Holla, Roy Franz, Harb Abdulhamid, Nishanth Menon, Arnd Bergmann

The performance protocol is intended for the performance management of
group(s) of device(s) that run in the same performance domain. It
includes even the CPUs. A performance domain is defined by a set of
devices that always have to run at the same performance level.
For example, a set of CPUs that share a voltage domain, and have a
common frequency control, is said to be in the same performance domain.

The commands in this protocol provide functionality to describe the
protocol version, describe various attribute flags, set and get the
performance level of a domain. It also supports discovery of the list
of performance levels supported by a performance domain, and the
properties of each performance level.

This patch adds basic support for the performance protocol.

Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
---
 drivers/firmware/arm_scmi/Makefile |   2 +-
 drivers/firmware/arm_scmi/common.h |   1 +
 drivers/firmware/arm_scmi/perf.c   | 398 +++++++++++++++++++++++++++++++++++++
 include/linux/scmi_protocol.h      |  32 +++
 4 files changed, 432 insertions(+), 1 deletion(-)
 create mode 100644 drivers/firmware/arm_scmi/perf.c

diff --git a/drivers/firmware/arm_scmi/Makefile b/drivers/firmware/arm_scmi/Makefile
index 21d01d1d6b9c..159de726ee45 100644
--- a/drivers/firmware/arm_scmi/Makefile
+++ b/drivers/firmware/arm_scmi/Makefile
@@ -1,2 +1,2 @@
 obj-$(CONFIG_ARM_SCMI_PROTOCOL)	= arm_scmi.o
-arm_scmi-y = base.o driver.o
+arm_scmi-y = base.o driver.o perf.o
diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h
index 24bc51dcc6c5..e153f05720e4 100644
--- a/drivers/firmware/arm_scmi/common.h
+++ b/drivers/firmware/arm_scmi/common.h
@@ -30,6 +30,7 @@
 #define PROTOCOL_REV_MAJOR(x)	((x) >> PROTOCOL_REV_MINOR_BITS)
 #define PROTOCOL_REV_MINOR(x)	((x) & PROTOCOL_REV_MINOR_MASK)
 #define MAX_PROTOCOLS_IMP	16
+#define MAX_OPPS		16
 
 enum scmi_std_protocol {
 	SCMI_PROTOCOL_BASE = 0x10,
diff --git a/drivers/firmware/arm_scmi/perf.c b/drivers/firmware/arm_scmi/perf.c
new file mode 100644
index 000000000000..25a21bb5c3ef
--- /dev/null
+++ b/drivers/firmware/arm_scmi/perf.c
@@ -0,0 +1,398 @@
+/*
+ * System Control and Management Interface (SCMI) Performance Protocol
+ *
+ * Copyright (C) 2017 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "common.h"
+
+enum scmi_performance_protocol_cmd {
+	PERF_DOMAIN_ATTRIBUTES = 0x3,
+	PERF_DESCRIBE_LEVELS = 0x4,
+	PERF_LIMITS_SET = 0x5,
+	PERF_LIMITS_GET = 0x6,
+	PERF_LEVEL_SET = 0x7,
+	PERF_LEVEL_GET = 0x8,
+	PERF_NOTIFY_LIMITS = 0x9,
+	PERF_NOTIFY_LEVEL = 0xa,
+};
+
+struct scmi_msg_resp_perf_attributes {
+	__le16 num_domains;
+	__le16 flags;
+#define POWER_SCALE_IN_MILLIWATT(x)	((x) & BIT(0))
+	__le32 stats_addr_low;
+	__le32 stats_addr_high;
+	__le32 stats_size;
+} __packed;
+
+struct scmi_msg_resp_perf_domain_attributes {
+	__le32 flags;
+#define SUPPORTS_SET_LIMITS(x)		((x) & BIT(31))
+#define SUPPORTS_SET_PERF_LVL(x)	((x) & BIT(30))
+#define SUPPORTS_PERF_LIMIT_NOTIFY(x)	((x) & BIT(29))
+#define SUPPORTS_PERF_LEVEL_NOTIFY(x)	((x) & BIT(28))
+	__le16 rate_limit_us;
+	__le16 reserved3;
+	__le32 max_freq;
+	__le32 min_freq;
+#define FREQUENCY_BASE(x)		((x) >> 16)
+#define FREQUENCY_SCALE(x)		((x) & 0x3f)
+	    u8 name[SCMI_MAX_STR_SIZE];
+} __packed;
+
+struct scmi_msg_perf_describe_levels {
+	__le32 domain;
+	__le32 level_index;
+} __packed;
+
+struct scmi_perf_set_limits {
+	__le32 domain;
+	__le32 max_level;
+	__le32 min_level;
+} __packed;
+
+struct scmi_perf_get_limits {
+	__le32 max_level;
+	__le32 min_level;
+} __packed;
+
+struct scmi_perf_set_level {
+	__le32 domain;
+	__le32 level;
+} __packed;
+
+struct scmi_perf_notify_level_or_limits {
+	__le32 domain;
+	__le32 notify_enable;
+} __packed;
+
+struct scmi_msg_resp_perf_describe_levels {
+	__le16 num_returned;
+	__le16 num_remaining;
+	struct {
+		__le32 perf_val;
+		__le32 power;
+		__le16 transition_latency_us;
+		__le16 reserved;
+	} opp[0];
+} __packed;
+
+struct perf_dom_info {
+	bool set_limits;
+	bool set_perf;
+	bool perf_limit_notify;
+	bool perf_level_notify;
+	char name[SCMI_MAX_STR_SIZE];
+	struct scmi_opp opp[MAX_OPPS];
+};
+
+struct scmi_perf_info {
+	int num_domains;
+	bool power_scale_mw;
+	u64 stats_addr;
+	u32 stats_size;
+	struct perf_dom_info *dom_info;
+};
+
+static struct scmi_perf_info perf_info;
+
+static int scmi_perf_attributes_get(struct scmi_handle *handle,
+				    struct scmi_perf_info *perf_info)
+{
+	int ret;
+	struct scmi_xfer *t;
+	struct scmi_msg_resp_perf_attributes *attr;
+
+	ret = scmi_one_xfer_init(handle, PROTOCOL_ATTRIBUTES,
+				 SCMI_PROTOCOL_PERF, 0, sizeof(*attr), &t);
+	if (ret)
+		return ret;
+
+	attr = (struct scmi_msg_resp_perf_attributes *)t->rx.buf;
+
+	ret = scmi_do_xfer(handle, t);
+	if (!ret) {
+		u16 flags = le16_to_cpu(attr->flags);
+
+		perf_info->num_domains = le16_to_cpu(attr->num_domains);
+		perf_info->power_scale_mw = POWER_SCALE_IN_MILLIWATT(flags);
+		perf_info->stats_addr = le32_to_cpu(attr->stats_addr_low) |
+				(u64)le32_to_cpu(attr->stats_addr_high) << 32;
+		perf_info->stats_size = le32_to_cpu(attr->stats_size);
+	}
+
+	scmi_put_one_xfer(handle, t);
+	return ret;
+}
+
+static int
+scmi_perf_domain_attributes_get(struct scmi_handle *handle, u32 domain,
+				struct perf_dom_info *dom_info)
+{
+	int ret;
+	struct scmi_xfer *t;
+	struct scmi_msg_resp_perf_domain_attributes *attr;
+
+	ret = scmi_one_xfer_init(handle, PERF_DOMAIN_ATTRIBUTES,
+				 SCMI_PROTOCOL_PERF, sizeof(domain),
+				 sizeof(*attr), &t);
+	if (ret)
+		return ret;
+
+	*(__le32 *)t->tx.buf = cpu_to_le32(domain);
+	attr = (struct scmi_msg_resp_perf_domain_attributes *)t->rx.buf;
+
+	ret = scmi_do_xfer(handle, t);
+	if (!ret) {
+		u32 flags = le32_to_cpu(attr->flags);
+
+		dom_info->set_limits = SUPPORTS_SET_LIMITS(flags);
+		dom_info->set_perf = SUPPORTS_SET_PERF_LVL(flags);
+		dom_info->perf_limit_notify = SUPPORTS_PERF_LIMIT_NOTIFY(flags);
+		dom_info->perf_level_notify = SUPPORTS_PERF_LEVEL_NOTIFY(flags);
+		memcpy(dom_info->name, attr->name, SCMI_MAX_STR_SIZE);
+	}
+
+	scmi_put_one_xfer(handle, t);
+	return ret;
+}
+
+static int scmi_perf_describe_levels_get(struct scmi_handle *handle, u32 domain,
+					 struct perf_dom_info *perf_dom)
+{
+	int ret, cnt;
+	u32 tot_opp_cnt = 0;
+	u16 num_returned, num_remaining;
+	struct scmi_xfer *t;
+	struct scmi_opp *opp;
+	struct scmi_msg_perf_describe_levels *dom_info;
+	struct scmi_msg_resp_perf_describe_levels *level_info;
+
+	ret = scmi_one_xfer_init(handle, PERF_DESCRIBE_LEVELS,
+				 SCMI_PROTOCOL_PERF, sizeof(*dom_info), 0, &t);
+	if (ret)
+		return ret;
+
+	dom_info = (struct scmi_msg_perf_describe_levels *)t->tx.buf;
+	level_info = (struct scmi_msg_resp_perf_describe_levels *)t->rx.buf;
+
+	do {
+		dom_info->domain = cpu_to_le32(domain);
+		/* Set the number of OPPs to be skipped/already read */
+		dom_info->level_index = cpu_to_le32(tot_opp_cnt);
+
+		ret = scmi_do_xfer(handle, t);
+		if (ret)
+			break;
+
+		num_returned = le16_to_cpu(level_info->num_returned);
+		num_remaining = le16_to_cpu(level_info->num_remaining);
+		if (tot_opp_cnt + num_returned > MAX_OPPS) {
+			dev_err(handle->dev, "No. of OPPs exceeded MAX_OPPS");
+			break;
+		}
+
+		opp = &perf_dom->opp[tot_opp_cnt];
+		for (cnt = 0; cnt < num_returned; cnt++, opp++) {
+			opp->freq = le32_to_cpu(level_info->opp[cnt].perf_val);
+			opp->volt = le32_to_cpu(level_info->opp[cnt].power);
+			opp->trans_latency_us = le16_to_cpu(
+				level_info->opp[cnt].transition_latency_us);
+
+			dev_dbg(handle->dev, "Level %d Power %d Latency %dus\n",
+				opp->freq, opp->volt, opp->trans_latency_us);
+		}
+
+		tot_opp_cnt += num_returned;
+		/*
+		 * check for both returned and remaining to avoid infinite
+		 * loop due to buggy firmware
+		 */
+	} while (num_returned && num_remaining);
+
+	scmi_put_one_xfer(handle, t);
+	return ret;
+}
+
+static int scmi_perf_limits_set(struct scmi_handle *handle, u32 domain,
+				u32 max_perf, u32 min_perf)
+{
+	int ret;
+	struct scmi_xfer *t;
+	struct scmi_perf_set_limits *limits;
+
+	ret = scmi_one_xfer_init(handle, PERF_LIMITS_SET, SCMI_PROTOCOL_PERF,
+				 sizeof(*limits), 0, &t);
+	if (ret)
+		return ret;
+
+	limits = (struct scmi_perf_set_limits *)t->tx.buf;
+	limits->domain = cpu_to_le32(domain);
+	limits->max_level = cpu_to_le32(max_perf);
+	limits->min_level = cpu_to_le32(min_perf);
+
+	ret = scmi_do_xfer(handle, t);
+
+	scmi_put_one_xfer(handle, t);
+	return ret;
+}
+
+static int scmi_perf_limits_get(struct scmi_handle *handle, u32 domain,
+				u32 *max_perf, u32 *min_perf)
+{
+	int ret;
+	struct scmi_xfer *t;
+	struct scmi_perf_get_limits *limits;
+
+	ret = scmi_one_xfer_init(handle, PERF_LIMITS_GET, SCMI_PROTOCOL_PERF,
+				 sizeof(__le32), 0, &t);
+	if (ret)
+		return ret;
+
+	*(__le32 *)t->tx.buf = cpu_to_le32(domain);
+
+	ret = scmi_do_xfer(handle, t);
+	if (!ret) {
+		limits = (struct scmi_perf_get_limits *)t->rx.buf;
+
+		*max_perf = le32_to_cpu(limits->max_level);
+		*min_perf = le32_to_cpu(limits->min_level);
+	}
+
+	scmi_put_one_xfer(handle, t);
+	return ret;
+}
+
+static int
+scmi_perf_level_set(struct scmi_handle *handle, u32 domain, u32 level)
+{
+	int ret;
+	struct scmi_xfer *t;
+	struct scmi_perf_set_level *lvl;
+
+	ret = scmi_one_xfer_init(handle, PERF_LEVEL_SET, SCMI_PROTOCOL_PERF,
+				 sizeof(*lvl), 0, &t);
+	if (ret)
+		return ret;
+
+	lvl = (struct scmi_perf_set_level *)t->tx.buf;
+	lvl->domain = cpu_to_le32(domain);
+	lvl->level = cpu_to_le32(level);
+
+	ret = scmi_do_xfer(handle, t);
+
+	scmi_put_one_xfer(handle, t);
+	return ret;
+}
+
+static int
+scmi_perf_level_get(struct scmi_handle *handle, u32 domain, u32 *level)
+{
+	int ret;
+	struct scmi_xfer *t;
+
+	ret = scmi_one_xfer_init(handle, PERF_LEVEL_GET, SCMI_PROTOCOL_PERF,
+				 sizeof(u32), sizeof(u32), &t);
+	if (ret)
+		return ret;
+
+	*(__le32 *)t->tx.buf = cpu_to_le32(domain);
+
+	ret = scmi_do_xfer(handle, t);
+	if (!ret)
+		*level = le32_to_cpu(*(__le32 *)t->rx.buf);
+
+	scmi_put_one_xfer(handle, t);
+	return ret;
+}
+
+static int __scmi_perf_notify_enable(struct scmi_handle *handle, u32 cmd,
+				     u32 domain, bool enable)
+{
+	int ret;
+	struct scmi_xfer *t;
+	struct scmi_perf_notify_level_or_limits *notify;
+
+	ret = scmi_one_xfer_init(handle, cmd, SCMI_PROTOCOL_PERF,
+				 sizeof(*notify), 0, &t);
+	if (ret)
+		return ret;
+
+	notify = (struct scmi_perf_notify_level_or_limits *)t->tx.buf;
+	notify->domain = cpu_to_le32(domain);
+	notify->notify_enable = cpu_to_le32(enable & BIT(0));
+
+	ret = scmi_do_xfer(handle, t);
+
+	scmi_put_one_xfer(handle, t);
+	return ret;
+}
+
+static int
+scmi_perf_limits_notify_enable(struct scmi_handle *handle, u32 dom, bool en)
+{
+	return __scmi_perf_notify_enable(handle, PERF_NOTIFY_LIMITS, dom, en);
+}
+
+static int
+scmi_perf_level_notify_enable(struct scmi_handle *handle, u32 dom, bool en)
+{
+	return __scmi_perf_notify_enable(handle, PERF_NOTIFY_LEVEL, dom, en);
+}
+
+static struct scmi_perf_ops perf_ops = {
+	.limits_set = scmi_perf_limits_set,
+	.limits_get = scmi_perf_limits_get,
+	.level_set = scmi_perf_level_set,
+	.level_get = scmi_perf_level_get,
+	.limits_notify_enable = scmi_perf_limits_notify_enable,
+	.level_notify_enable = scmi_perf_level_notify_enable,
+};
+
+int scmi_perf_protocol_init(struct scmi_handle *handle)
+{
+	int domain;
+	u32 version;
+
+	if (!scmi_is_protocol_implemented(handle, SCMI_PROTOCOL_PERF)) {
+		dev_err(handle->dev, "SCMI Perf protocol not implemented\n");
+		return -EPROTONOSUPPORT;
+	}
+
+	scmi_version_get(handle, SCMI_PROTOCOL_PERF, &version);
+
+	dev_dbg(handle->dev, "Performance Version %d.%d\n",
+		PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
+
+	scmi_perf_attributes_get(handle, &perf_info);
+
+	perf_info.dom_info = devm_kcalloc(handle->dev, perf_info.num_domains,
+					     sizeof(struct perf_dom_info),
+					     GFP_KERNEL);
+	if (!perf_info.dom_info)
+		return -ENOMEM;
+
+	for (domain = 0; domain < perf_info.num_domains; domain++) {
+		struct perf_dom_info *dom = perf_info.dom_info + domain;
+
+		scmi_perf_domain_attributes_get(handle, domain, dom);
+		scmi_perf_describe_levels_get(handle, domain, dom);
+	}
+
+	handle->perf_ops = &perf_ops;
+
+	return 0;
+}
diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h
index 901976fe211f..0f25a3defb4c 100644
--- a/include/linux/scmi_protocol.h
+++ b/include/linux/scmi_protocol.h
@@ -43,15 +43,47 @@ struct scmi_revision_info {
 	char sub_vendor_id[SCMI_MAX_STR_SIZE];
 };
 
+struct scmi_handle;
+
+/**
+ * struct scmi_perf_ops - represents the various operations provided
+ *	by SCMI Performance Protocol
+ *
+ * @limits_set: sets limits on the performance level of a domain
+ * @limits_get: gets limits on the performance level of a domain
+ * @level_set: sets the performance level of a domain
+ * @level_get: gets the performance level of a domain
+ * @limits_notify_enable: requests notifications from the platform for changes
+ *	in the allowed maximum and minimum performance levels
+ * @level_notify_enable: requests notifications from the platform when the
+ *	performance level for a domain changes in value
+ */
+struct scmi_perf_ops {
+	int (*limits_set)(struct scmi_handle *, u32, u32, u32);
+	int (*limits_get)(struct scmi_handle *, u32, u32 *, u32 *);
+	int (*level_set)(struct scmi_handle *, u32, u32 level);
+	int (*level_get)(struct scmi_handle *, u32, u32 *);
+	int (*limits_notify_enable)(struct scmi_handle *, u32, bool);
+	int (*level_notify_enable)(struct scmi_handle *, u32, bool);
+};
+
 /**
  * struct scmi_handle - Handle returned to ARM SCMI clients for usage.
  *
  * @dev: pointer to the SCMI device
  * @version: pointer to the structure containing SCMI version information
+ * @perf_ops: pointer to set of performance protocol operations
  */
 struct scmi_handle {
 	struct device *dev;
 	struct scmi_revision_info *version;
+	struct scmi_perf_ops *perf_ops;
+};
+
+struct scmi_opp {
+	u32 freq;
+	u32 volt;
+	u32 trans_latency_us;
 };
 
 #if IS_REACHABLE(CONFIG_ARM_SCMI_PROTOCOL)
-- 
2.7.4

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

* [RFC PATCH 5/8] firmware: arm_scmi: add initial support for clock protocol
  2017-06-07 16:10 [RFC PATCH 0/8] firmware: ARM System Control and Management Interface(SCMI) support Sudeep Holla
                   ` (3 preceding siblings ...)
  2017-06-07 16:10 ` [RFC PATCH 4/8] firmware: arm_scmi: add initial support for performance protocol Sudeep Holla
@ 2017-06-07 16:10 ` Sudeep Holla
  2017-06-07 19:19   ` Roy Franz
  2017-06-07 16:10 ` [RFC PATCH 6/8] firmware: arm_scmi: add initial support for power protocol Sudeep Holla
                   ` (2 subsequent siblings)
  7 siblings, 1 reply; 23+ messages in thread
From: Sudeep Holla @ 2017-06-07 16:10 UTC (permalink / raw)
  To: linux-kernel, devicetree
  Cc: Sudeep Holla, Roy Franz, Harb Abdulhamid, Nishanth Menon, Arnd Bergmann

The clock protocol is intended for management of clocks. It is used to
enable or disable clocks, and to set and get the clock rates. This
protocol provides commands to describe the protocol version, discover
various implementation specific attributes, describe a clock, enable
and disable a clock and get/set the rate of the clock synchronously or
asynchronously.

This patch adds initial support for the clock protocol.

Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
---
 drivers/firmware/arm_scmi/Makefile |   2 +-
 drivers/firmware/arm_scmi/clock.c  | 340 +++++++++++++++++++++++++++++++++++++
 drivers/firmware/arm_scmi/common.h |   1 +
 include/linux/scmi_protocol.h      |  18 ++
 4 files changed, 360 insertions(+), 1 deletion(-)
 create mode 100644 drivers/firmware/arm_scmi/clock.c

diff --git a/drivers/firmware/arm_scmi/Makefile b/drivers/firmware/arm_scmi/Makefile
index 159de726ee45..6836b1f38f7f 100644
--- a/drivers/firmware/arm_scmi/Makefile
+++ b/drivers/firmware/arm_scmi/Makefile
@@ -1,2 +1,2 @@
 obj-$(CONFIG_ARM_SCMI_PROTOCOL)	= arm_scmi.o
-arm_scmi-y = base.o driver.o perf.o
+arm_scmi-y = base.o clock.o driver.o perf.o
diff --git a/drivers/firmware/arm_scmi/clock.c b/drivers/firmware/arm_scmi/clock.c
new file mode 100644
index 000000000000..e02827a48ab7
--- /dev/null
+++ b/drivers/firmware/arm_scmi/clock.c
@@ -0,0 +1,340 @@
+/*
+ * System Control and Management Interface (SCMI) Clock Protocol
+ *
+ * Copyright (C) 2017 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "common.h"
+
+enum scmi_clock_protocol_cmd {
+	CLOCK_ATTRIBUTES = 0x3,
+	CLOCK_DESCRIBE_RATES = 0x4,
+	CLOCK_RATE_SET = 0x5,
+	CLOCK_RATE_GET = 0x6,
+	CLOCK_CONFIG_SET = 0x7,
+};
+
+struct scmi_msg_resp_clock_protocol_attributes {
+	__le16 num_clocks;
+	    u8 max_async_req;
+	    u8 reserved;
+} __packed;
+
+struct scmi_msg_resp_clock_attributes {
+	__le32 attributes;
+#define	CLOCK_ENABLE	BIT(0)
+	    u8 name[SCMI_MAX_STR_SIZE];
+} __packed;
+
+struct scmi_clock_set_config {
+	__le32 id;
+	__le32 attributes;
+} __packed;
+
+struct scmi_msg_clock_describe_rates {
+	__le32 id;
+	__le32 rate_index;
+} __packed;
+
+struct scmi_msg_resp_clock_describe_rates {
+	__le16 num_returned;
+#define NUM_RETURNED_MASK	(0xfff)
+#define RATE_DISCRETE(x)	((x) & BIT(12))
+	__le16 num_remaining;
+	struct {
+		__le32 value_low;
+		__le32 value_high;
+	} rate[0];
+#define RATE_TO_U64(X)		\
+({				\
+	typeof(X) x = (X);	\
+	le32_to_cpu((x).value_low) | (u64)le32_to_cpu((x).value_high) >> 32; \
+})
+} __packed;
+
+struct scmi_clock_set_rate {
+	__le32 flags;
+#define CLOCK_SET_ASYSC		BIT(0)
+#define CLOCK_SET_DELAYED	BIT(1)
+#define CLOCK_ROUND_UP		BIT(2)
+#define CLOCK_ROUND_AUTO	BIT(3)
+	__le32 id;
+	__le32 value_low;
+	__le32 value_high;
+} __packed;
+
+struct clock_info {
+	u32 attributes;
+	char name[SCMI_MAX_STR_SIZE];
+	union {
+		int num_rates;
+		u64 rates[MAX_NUM_RATES];
+		struct {
+			u64 min_rate;
+			u64 max_rate;
+			u64 step_size;
+		} range;
+	};
+};
+
+struct scmi_clock_info {
+	int num_clocks;
+	int max_async_req;
+	struct clock_info *clk;
+};
+
+static struct scmi_clock_info clocks;
+
+static int scmi_clock_protocol_attributes_get(struct scmi_handle *handle,
+					      struct scmi_clock_info *clocks)
+{
+	int ret;
+	struct scmi_xfer *t;
+	struct scmi_msg_resp_clock_protocol_attributes *attr;
+
+	ret = scmi_one_xfer_init(handle, PROTOCOL_ATTRIBUTES,
+				 SCMI_PROTOCOL_CLOCK, 0, sizeof(*attr), &t);
+	if (ret)
+		return ret;
+
+	attr = (struct scmi_msg_resp_clock_protocol_attributes *)t->rx.buf;
+
+	ret = scmi_do_xfer(handle, t);
+	if (!ret) {
+		clocks->num_clocks = le16_to_cpu(attr->num_clocks);
+		clocks->max_async_req = attr->max_async_req;
+	}
+
+	scmi_put_one_xfer(handle, t);
+	return ret;
+}
+
+static int scmi_clock_attributes_get(struct scmi_handle *handle, u32 clk_id,
+				     struct clock_info *clk)
+{
+	int ret;
+	struct scmi_xfer *t;
+	struct scmi_msg_resp_clock_attributes *attr;
+
+	ret = scmi_one_xfer_init(handle, CLOCK_ATTRIBUTES, SCMI_PROTOCOL_CLOCK,
+				 sizeof(clk_id), sizeof(*attr), &t);
+	if (ret)
+		return ret;
+
+	*(__le32 *)t->tx.buf = cpu_to_le32(clk_id);
+	attr = (struct scmi_msg_resp_clock_attributes *)t->rx.buf;
+
+	ret = scmi_do_xfer(handle, t);
+	if (!ret) {
+		clk->attributes = le32_to_cpu(attr->attributes);
+		memcpy(clk->name, attr->name, SCMI_MAX_STR_SIZE);
+	}
+
+	scmi_put_one_xfer(handle, t);
+	return ret;
+}
+
+static int scmi_clock_describe_rates_get(struct scmi_handle *handle, u32 clk_id,
+					 struct clock_info *clk)
+{
+	u64 *rate;
+	int ret, cnt;
+	bool rate_discrete;
+	u32 tot_rate_cnt = 0;
+	u16 num_returned, num_remaining;
+	struct scmi_xfer *t;
+	struct scmi_msg_clock_describe_rates *clk_desc;
+	struct scmi_msg_resp_clock_describe_rates *rlist;
+
+	ret = scmi_one_xfer_init(handle, CLOCK_DESCRIBE_RATES,
+				 SCMI_PROTOCOL_CLOCK, sizeof(*clk_desc), 0, &t);
+	if (ret)
+		return ret;
+
+	clk_desc = (struct scmi_msg_clock_describe_rates *)t->tx.buf;
+	rlist = (struct scmi_msg_resp_clock_describe_rates *)t->rx.buf;
+
+	do {
+		clk_desc->id = cpu_to_le32(clk_id);
+		/* Set the number of rates to be skipped/already read */
+		clk_desc->rate_index = cpu_to_le32(tot_rate_cnt);
+
+		ret = scmi_do_xfer(handle, t);
+		if (ret)
+			break;
+
+		num_returned = le16_to_cpu(rlist->num_returned);
+		num_remaining = le16_to_cpu(rlist->num_remaining);
+		rate_discrete = RATE_DISCRETE(num_remaining);
+		num_remaining &= NUM_RETURNED_MASK;
+
+		if (tot_rate_cnt + num_returned > MAX_NUM_RATES) {
+			dev_err(handle->dev, "No. of rates > MAX_NUM_RATES");
+			break;
+		}
+
+		if (!rate_discrete) {
+			clk->range.min_rate = RATE_TO_U64(rlist->rate[0]);
+			clk->range.max_rate = RATE_TO_U64(rlist->rate[1]);
+			clk->range.step_size = RATE_TO_U64(rlist->rate[2]);
+			dev_dbg(handle->dev, "Min %llu Max %llu Step %llu Hz\n",
+				clk->range.min_rate, clk->range.max_rate,
+				clk->range.step_size);
+			break;
+		}
+
+		rate = &clk->rates[tot_rate_cnt];
+		for (cnt = 0; cnt < num_returned; cnt++, rate++) {
+			*rate = RATE_TO_U64(rlist->rate[cnt]);
+			dev_dbg(handle->dev, "Rate %llu Hz\n", *rate);
+		}
+
+		tot_rate_cnt += num_returned;
+		/*
+		 * check for both returned and remaining to avoid infinite
+		 * loop due to buggy firmware
+		 */
+	} while (num_returned && num_remaining);
+
+	clk->num_rates = tot_rate_cnt;
+
+	scmi_put_one_xfer(handle, t);
+	return ret;
+}
+
+static int
+scmi_clock_rate_get(struct scmi_handle *handle, u32 clk_id, u64 *value)
+{
+	int ret;
+	struct scmi_xfer *t;
+
+	ret = scmi_one_xfer_init(handle, CLOCK_RATE_GET, SCMI_PROTOCOL_CLOCK,
+				 sizeof(__le32), sizeof(u64), &t);
+	if (ret)
+		return ret;
+
+	*(__le32 *)t->tx.buf = cpu_to_le32(clk_id);
+
+	ret = scmi_do_xfer(handle, t);
+	if (!ret) {
+		__le32 *pval = (__le32 *)t->rx.buf;
+
+		*value = le32_to_cpu(*pval);
+		*value  |= le32_to_cpu(*(pval + 1));
+	}
+
+	scmi_put_one_xfer(handle, t);
+	return ret;
+}
+
+static int scmi_clock_rate_set(struct scmi_handle *handle, u32 clk_id,
+			       u32 config, u64 rate)
+{
+	int ret;
+	struct scmi_xfer *t;
+	struct scmi_clock_set_rate *cfg;
+
+	ret = scmi_one_xfer_init(handle, CLOCK_RATE_SET, SCMI_PROTOCOL_CLOCK,
+				 sizeof(*cfg), 0, &t);
+	if (ret)
+		return ret;
+
+	cfg = (struct scmi_clock_set_rate *)t->tx.buf;
+	cfg->flags = cpu_to_le32(config);
+	cfg->id = cpu_to_le32(clk_id);
+	cfg->value_low = cpu_to_le32(rate & 0xffffffff);
+	cfg->value_high = cpu_to_le32(rate >> 32);
+
+	ret = scmi_do_xfer(handle, t);
+
+	scmi_put_one_xfer(handle, t);
+	return ret;
+}
+
+static int
+scmi_clock_config_set(struct scmi_handle *handle, u32 clk_id, u32 config)
+{
+	int ret;
+	struct scmi_xfer *t;
+	struct scmi_clock_set_config *cfg;
+
+	ret = scmi_one_xfer_init(handle, CLOCK_CONFIG_SET, SCMI_PROTOCOL_CLOCK,
+				 sizeof(*cfg), 0, &t);
+	if (ret)
+		return ret;
+
+	cfg = (struct scmi_clock_set_config *)t->tx.buf;
+	cfg->id = cpu_to_le32(clk_id);
+	cfg->attributes = cpu_to_le32(config);
+
+	ret = scmi_do_xfer(handle, t);
+
+	scmi_put_one_xfer(handle, t);
+	return ret;
+}
+
+static int scmi_clock_enable(struct scmi_handle *handle, u32 clk_id)
+{
+	return scmi_clock_config_set(handle, clk_id, CLOCK_ENABLE);
+}
+
+static int scmi_clock_disable(struct scmi_handle *handle, u32 clk_id)
+{
+	return scmi_clock_config_set(handle, clk_id, 0);
+}
+
+static struct scmi_clk_ops clk_ops = {
+	.rate_get = scmi_clock_rate_get,
+	.rate_set = scmi_clock_rate_set,
+	.enable = scmi_clock_enable,
+	.disable = scmi_clock_disable,
+};
+
+int scmi_clock_protocol_init(struct scmi_handle *handle)
+{
+	int clk_id;
+	u32 version;
+
+	if (!scmi_is_protocol_implemented(handle, SCMI_PROTOCOL_CLOCK)) {
+		dev_err(handle->dev, "SCMI Clock protocol not implemented\n");
+		return -EPROTONOSUPPORT;
+	}
+
+	scmi_version_get(handle, SCMI_PROTOCOL_CLOCK, &version);
+
+	dev_dbg(handle->dev, "Clock Version %d.%d\n",
+		PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
+
+	scmi_clock_protocol_attributes_get(handle, &clocks);
+
+	clocks.clk = devm_kcalloc(handle->dev, clocks.num_clocks,
+				  sizeof(struct clock_info), GFP_KERNEL);
+	if (!clocks.clk)
+		return -ENOMEM;
+
+	dev_info(handle->dev, "Num Clock %d Max Async Req %d\n",
+		 clocks.num_clocks, clocks.max_async_req);
+
+	for (clk_id = 0; clk_id < clocks.num_clocks; clk_id++) {
+		struct clock_info *clk = clocks.clk + clk_id;
+
+		scmi_clock_attributes_get(handle, clk_id, clk);
+		scmi_clock_describe_rates_get(handle, clk_id, clk);
+	}
+
+	handle->clk_ops = &clk_ops;
+
+	return 0;
+}
diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h
index e153f05720e4..32873a315c76 100644
--- a/drivers/firmware/arm_scmi/common.h
+++ b/drivers/firmware/arm_scmi/common.h
@@ -31,6 +31,7 @@
 #define PROTOCOL_REV_MINOR(x)	((x) & PROTOCOL_REV_MINOR_MASK)
 #define MAX_PROTOCOLS_IMP	16
 #define MAX_OPPS		16
+#define MAX_NUM_RATES		16
 
 enum scmi_std_protocol {
 	SCMI_PROTOCOL_BASE = 0x10,
diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h
index 0f25a3defb4c..75ba10441f54 100644
--- a/include/linux/scmi_protocol.h
+++ b/include/linux/scmi_protocol.h
@@ -46,6 +46,22 @@ struct scmi_revision_info {
 struct scmi_handle;
 
 /**
+ * struct scmi_clk_ops - represents the various operations provided
+ *	by SCMI Clock Protocol
+ *
+ * @rate_get: request the current clock rate of a clock
+ * @rate_set: set the clock rate of a clock
+ * @enable: enables the specified clock
+ * @disable: disables the specified clock
+ */
+struct scmi_clk_ops {
+	int (*rate_get)(struct scmi_handle *, u32, u64*);
+	int (*rate_set)(struct scmi_handle *, u32, u32, u64);
+	int (*enable)(struct scmi_handle *, u32);
+	int (*disable)(struct scmi_handle *, u32);
+};
+
+/**
  * struct scmi_perf_ops - represents the various operations provided
  *	by SCMI Performance Protocol
  *
@@ -73,11 +89,13 @@ struct scmi_perf_ops {
  * @dev: pointer to the SCMI device
  * @version: pointer to the structure containing SCMI version information
  * @perf_ops: pointer to set of performance protocol operations
+ * @clk_ops: pointer to set of clock protocol operations
  */
 struct scmi_handle {
 	struct device *dev;
 	struct scmi_revision_info *version;
 	struct scmi_perf_ops *perf_ops;
+	struct scmi_clk_ops *clk_ops;
 };
 
 struct scmi_opp {
-- 
2.7.4

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

* [RFC PATCH 6/8] firmware: arm_scmi: add initial support for power protocol
  2017-06-07 16:10 [RFC PATCH 0/8] firmware: ARM System Control and Management Interface(SCMI) support Sudeep Holla
                   ` (4 preceding siblings ...)
  2017-06-07 16:10 ` [RFC PATCH 5/8] firmware: arm_scmi: add initial support for clock protocol Sudeep Holla
@ 2017-06-07 16:10 ` Sudeep Holla
  2017-06-07 20:38   ` Arnd Bergmann
  2017-06-07 16:10 ` [RFC PATCH 7/8] firmware: arm_scmi: add initial support for sensor protocol Sudeep Holla
  2017-06-07 16:10 ` [RFC PATCH 8/8] firmware: arm_scmi: probe and initialise all the supported protocols Sudeep Holla
  7 siblings, 1 reply; 23+ messages in thread
From: Sudeep Holla @ 2017-06-07 16:10 UTC (permalink / raw)
  To: linux-kernel, devicetree
  Cc: Sudeep Holla, Roy Franz, Harb Abdulhamid, Nishanth Menon, Arnd Bergmann

The power protocol is intended for management of power states of various
power domains. The power domain management protocol provides commands to
describe the protocol version, discover the implementation specific
attributes, set and get the power state of a domain.

This patch adds support for the above mention features of the protocol.

Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
---
 drivers/firmware/arm_scmi/Makefile |   2 +-
 drivers/firmware/arm_scmi/power.c  | 237 +++++++++++++++++++++++++++++++++++++
 include/linux/scmi_protocol.h      |  17 +++
 3 files changed, 255 insertions(+), 1 deletion(-)
 create mode 100644 drivers/firmware/arm_scmi/power.c

diff --git a/drivers/firmware/arm_scmi/Makefile b/drivers/firmware/arm_scmi/Makefile
index 6836b1f38f7f..52ecc08556a2 100644
--- a/drivers/firmware/arm_scmi/Makefile
+++ b/drivers/firmware/arm_scmi/Makefile
@@ -1,2 +1,2 @@
 obj-$(CONFIG_ARM_SCMI_PROTOCOL)	= arm_scmi.o
-arm_scmi-y = base.o clock.o driver.o perf.o
+arm_scmi-y = base.o clock.o driver.o perf.o power.o
diff --git a/drivers/firmware/arm_scmi/power.c b/drivers/firmware/arm_scmi/power.c
new file mode 100644
index 000000000000..37d5abfb9d87
--- /dev/null
+++ b/drivers/firmware/arm_scmi/power.c
@@ -0,0 +1,237 @@
+/*
+ * System Control and Management Interface (SCMI) Power Protocol
+ *
+ * Copyright (C) 2017 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "common.h"
+
+enum scmi_power_protocol_cmd {
+	POWER_DOMAIN_ATTRIBUTES = 0x3,
+	POWER_STATE_SET = 0x4,
+	POWER_STATE_GET = 0x5,
+	POWER_STATE_NOTIFY = 0x6,
+};
+
+struct scmi_msg_resp_power_attributes {
+	__le16 num_domains;
+	__le16 reserved;
+	__le32 stats_addr_low;
+	__le32 stats_addr_high;
+	__le32 stats_size;
+} __packed;
+
+struct scmi_msg_resp_power_domain_attributes {
+	__le32 flags;
+#define SUPPORTS_STATE_SET_NOTIFY(x)	((x) & BIT(31))
+#define SUPPORTS_STATE_SET_ASYNC(x)	((x) & BIT(30))
+#define SUPPORTS_STATE_SET_SYNC(x)	((x) & BIT(29))
+	    u8 name[SCMI_MAX_STR_SIZE];
+} __packed;
+
+struct scmi_power_set_state {
+	__le32 flags;
+#define STATE_SET_ASYNC		BIT(0)
+	__le32 domain;
+	__le32 state;
+#define STATE_TYPE_SHIFT	30
+#define STATE_ID_MASK		(BIT(28) - 1)
+#define POWER_STATE_PARAM(type, id) \
+	((((type) & BIT(0)) << STATE_TYPE_SHIFT) | ((id) & STATE_ID_MASK))
+} __packed;
+
+struct scmi_power_state_notify {
+	__le32 domain;
+	__le32 notify_enable;
+} __packed;
+
+struct power_dom_info {
+	bool state_set_sync;
+	bool state_set_async;
+	bool state_set_notify;
+	char name[SCMI_MAX_STR_SIZE];
+};
+
+struct scmi_power_info {
+	int num_domains;
+	u64 stats_addr;
+	u32 stats_size;
+	struct power_dom_info *dom_info;
+};
+
+static struct scmi_power_info power_info;
+
+static int scmi_power_attributes_get(struct scmi_handle *handle,
+				     struct scmi_power_info *power_info)
+{
+	int ret;
+	struct scmi_xfer *t;
+	struct scmi_msg_resp_power_attributes *attr;
+
+	ret = scmi_one_xfer_init(handle, PROTOCOL_ATTRIBUTES,
+				 SCMI_PROTOCOL_POWER, 0, sizeof(*attr), &t);
+	if (ret)
+		return ret;
+
+	attr = (struct scmi_msg_resp_power_attributes *)t->rx.buf;
+
+	ret = scmi_do_xfer(handle, t);
+	if (!ret) {
+		power_info->num_domains = le16_to_cpu(attr->num_domains);
+		power_info->stats_addr = le32_to_cpu(attr->stats_addr_low) |
+				(u64)le32_to_cpu(attr->stats_addr_high) << 32;
+		power_info->stats_size = le32_to_cpu(attr->stats_size);
+	}
+
+	scmi_put_one_xfer(handle, t);
+	return ret;
+}
+
+static int
+scmi_power_domain_attributes_get(struct scmi_handle *handle, u32 domain,
+				 struct power_dom_info *dom_info)
+{
+	int ret;
+	struct scmi_xfer *t;
+	struct scmi_msg_resp_power_domain_attributes *attr;
+
+	ret = scmi_one_xfer_init(handle, POWER_DOMAIN_ATTRIBUTES,
+				 SCMI_PROTOCOL_POWER, sizeof(domain),
+				 sizeof(*attr), &t);
+	if (ret)
+		return ret;
+
+	*(__le32 *)t->tx.buf = cpu_to_le32(domain);
+	attr = (struct scmi_msg_resp_power_domain_attributes *)t->rx.buf;
+
+	ret = scmi_do_xfer(handle, t);
+	if (!ret) {
+		u32 flags = le32_to_cpu(attr->flags);
+
+		dom_info->state_set_notify = SUPPORTS_STATE_SET_NOTIFY(flags);
+		dom_info->state_set_async = SUPPORTS_STATE_SET_ASYNC(flags);
+		dom_info->state_set_sync = SUPPORTS_STATE_SET_SYNC(flags);
+		memcpy(dom_info->name, attr->name, SCMI_MAX_STR_SIZE);
+	}
+
+	scmi_put_one_xfer(handle, t);
+	return ret;
+}
+
+static int
+scmi_power_state_set(struct scmi_handle *handle, u32 domain, u32 state)
+{
+	int ret;
+	struct scmi_xfer *t;
+	struct scmi_power_set_state *st;
+
+	ret = scmi_one_xfer_init(handle, POWER_STATE_SET, SCMI_PROTOCOL_POWER,
+				 sizeof(*st), 0, &t);
+	if (ret)
+		return ret;
+
+	st = (struct scmi_power_set_state *)t->tx.buf;
+	st->flags = cpu_to_le32(0);
+	st->domain = cpu_to_le32(domain);
+	st->state = cpu_to_le32(state);
+
+	ret = scmi_do_xfer(handle, t);
+
+	scmi_put_one_xfer(handle, t);
+	return ret;
+}
+
+static int
+scmi_power_state_get(struct scmi_handle *handle, u32 domain, u32 *state)
+{
+	int ret;
+	struct scmi_xfer *t;
+
+	ret = scmi_one_xfer_init(handle, POWER_STATE_GET, SCMI_PROTOCOL_POWER,
+				 sizeof(u32), sizeof(u32), &t);
+	if (ret)
+		return ret;
+
+	*(__le32 *)t->tx.buf = cpu_to_le32(domain);
+
+	ret = scmi_do_xfer(handle, t);
+	if (!ret)
+		*state = le32_to_cpu(*(__le32 *)t->rx.buf);
+
+	scmi_put_one_xfer(handle, t);
+	return ret;
+}
+
+static int scmi_power_state_notify_enable(struct scmi_handle *handle,
+					  u32 domain, bool enable)
+{
+	int ret;
+	struct scmi_xfer *t;
+	struct scmi_power_state_notify *notify;
+
+	ret = scmi_one_xfer_init(handle, POWER_STATE_NOTIFY,
+				 SCMI_PROTOCOL_POWER, sizeof(*notify), 0, &t);
+	if (ret)
+		return ret;
+
+	notify = (struct scmi_power_state_notify *)t->tx.buf;
+	notify->domain = cpu_to_le32(domain);
+	notify->notify_enable = cpu_to_le32(enable & BIT(0));
+
+	ret = scmi_do_xfer(handle, t);
+
+	scmi_put_one_xfer(handle, t);
+	return ret;
+}
+
+static struct scmi_power_ops power_ops = {
+	.state_set = scmi_power_state_set,
+	.state_get = scmi_power_state_get,
+	.state_notify_enable = scmi_power_state_notify_enable,
+};
+
+int scmi_power_protocol_init(struct scmi_handle *handle)
+{
+	u32 version;
+	int domain;
+
+	if (!scmi_is_protocol_implemented(handle, SCMI_PROTOCOL_POWER)) {
+		dev_err(handle->dev, "SCMI Power protocol not implemented\n");
+		return -EPROTONOSUPPORT;
+	}
+
+	scmi_version_get(handle, SCMI_PROTOCOL_POWER, &version);
+
+	dev_dbg(handle->dev, "Power Version %d.%d\n",
+		PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
+
+	scmi_power_attributes_get(handle, &power_info);
+
+	power_info.dom_info = devm_kcalloc(handle->dev, power_info.num_domains,
+					     sizeof(struct power_dom_info),
+					     GFP_KERNEL);
+	if (!power_info.dom_info)
+		return -ENOMEM;
+
+	for (domain = 0; domain < power_info.num_domains; domain++) {
+		struct power_dom_info *dom = power_info.dom_info + domain;
+
+		scmi_power_domain_attributes_get(handle, domain, dom);
+	}
+
+	handle->power_ops = &power_ops;
+
+	return 0;
+}
diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h
index 75ba10441f54..50a5816a295d 100644
--- a/include/linux/scmi_protocol.h
+++ b/include/linux/scmi_protocol.h
@@ -84,16 +84,33 @@ struct scmi_perf_ops {
 };
 
 /**
+ * struct scmi_power_ops - represents the various operations provided
+ *	by SCMI Power Protocol
+ *
+ * @state_set: sets the power state of a power domain
+ * @state_get: gets the power state of a power domain
+ * @state_notify_enable: request notifications from the platform for
+ *	state changes in a specific power domain
+ */
+struct scmi_power_ops {
+	int (*state_set)(struct scmi_handle *, u32, u32);
+	int (*state_get)(struct scmi_handle *, u32, u32 *);
+	int (*state_notify_enable)(struct scmi_handle *, u32, bool);
+};
+
+/**
  * struct scmi_handle - Handle returned to ARM SCMI clients for usage.
  *
  * @dev: pointer to the SCMI device
  * @version: pointer to the structure containing SCMI version information
+ * @power_ops: pointer to set of power protocol operations
  * @perf_ops: pointer to set of performance protocol operations
  * @clk_ops: pointer to set of clock protocol operations
  */
 struct scmi_handle {
 	struct device *dev;
 	struct scmi_revision_info *version;
+	struct scmi_power_ops *power_ops;
 	struct scmi_perf_ops *perf_ops;
 	struct scmi_clk_ops *clk_ops;
 };
-- 
2.7.4

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

* [RFC PATCH 7/8] firmware: arm_scmi: add initial support for sensor protocol
  2017-06-07 16:10 [RFC PATCH 0/8] firmware: ARM System Control and Management Interface(SCMI) support Sudeep Holla
                   ` (5 preceding siblings ...)
  2017-06-07 16:10 ` [RFC PATCH 6/8] firmware: arm_scmi: add initial support for power protocol Sudeep Holla
@ 2017-06-07 16:10 ` Sudeep Holla
  2017-06-07 19:19   ` Roy Franz
  2017-06-07 16:10 ` [RFC PATCH 8/8] firmware: arm_scmi: probe and initialise all the supported protocols Sudeep Holla
  7 siblings, 1 reply; 23+ messages in thread
From: Sudeep Holla @ 2017-06-07 16:10 UTC (permalink / raw)
  To: linux-kernel, devicetree
  Cc: Sudeep Holla, Roy Franz, Harb Abdulhamid, Nishanth Menon, Arnd Bergmann

The sensor protocol provides functions to manage platform sensors, and
provides the commands to describe the protocol version and the various
attribute flags. It also provides commands to discover various sensors
implemented and managed by the platform, read any sensor synchronously
or asynchronously as allowed by the platform, program sensor attributes
and/or configurations, if applicable.

This patch adds support for most of the above features.

Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
---
 drivers/firmware/arm_scmi/Makefile  |   2 +-
 drivers/firmware/arm_scmi/sensors.c | 269 ++++++++++++++++++++++++++++++++++++
 include/linux/scmi_protocol.h       |  17 +++
 3 files changed, 287 insertions(+), 1 deletion(-)
 create mode 100644 drivers/firmware/arm_scmi/sensors.c

diff --git a/drivers/firmware/arm_scmi/Makefile b/drivers/firmware/arm_scmi/Makefile
index 52ecc08556a2..f9dee5ad0aa0 100644
--- a/drivers/firmware/arm_scmi/Makefile
+++ b/drivers/firmware/arm_scmi/Makefile
@@ -1,2 +1,2 @@
 obj-$(CONFIG_ARM_SCMI_PROTOCOL)	= arm_scmi.o
-arm_scmi-y = base.o clock.o driver.o perf.o power.o
+arm_scmi-y = base.o clock.o driver.o perf.o power.o sensors.o
diff --git a/drivers/firmware/arm_scmi/sensors.c b/drivers/firmware/arm_scmi/sensors.c
new file mode 100644
index 000000000000..d8cee8c6cb99
--- /dev/null
+++ b/drivers/firmware/arm_scmi/sensors.c
@@ -0,0 +1,269 @@
+/*
+ * System Control and Management Interface (SCMI) Sensor Protocol
+ *
+ * Copyright (C) 2017 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "common.h"
+
+enum scmi_sensor_protocol_cmd {
+	SENSOR_DESCRIPTION_GET = 0x3,
+	SENSOR_CONFIG_SET = 0x4,
+	SENSOR_TRIP_POINT_SET = 0x5,
+	SENSOR_READING_GET = 0x6,
+};
+
+struct scmi_msg_resp_sensor_attributes {
+	__le16 num_sensors;
+	    u8 max_requests;
+	    u8 reserved;
+	__le32 reg_addr_low;
+	__le32 reg_addr_high;
+	__le32 reg_size;
+} __packed;
+
+struct scmi_msg_resp_sensor_description {
+	__le16 num_returned;
+	__le16 num_remaining;
+	struct {
+		__le32 id;
+		__le32 attributes_low;
+#define SUPPORTS_ASYNC_READ(x)	((x) & BIT(31))
+#define NUM_TRIP_POINTS(x)	(((x) >> 4) & 0xff)
+		__le32 attributes_high;
+#define SENSOR_TYPE(x)		((x) & 0xff)
+#define SENSOR_SCALE(x)		(((x) >> 11) & 0x3f)
+#define SENSOR_UPDATE_SCALE(x)	(((x) >> 22) & 0x1f)
+#define SENSOR_UPDATE_BASE(x)	(((x) >> 27) & 0x1f)
+		    u8 name[SCMI_MAX_STR_SIZE];
+	} desc[0];
+} __packed;
+
+struct scmi_msg_set_sensor_config {
+	__le32 id;
+	__le32 event_control;
+} __packed;
+
+struct scmi_msg_set_sensor_trip_point {
+	__le32 id;
+	__le32 event_control;
+#define SENSOR_TP_EVENT_MASK	(0x3)
+#define SENSOR_TP_DISABLED	0x0
+#define SENSOR_TP_POSITIVE	0x1
+#define SENSOR_TP_NEGATIVE	0x2
+#define SENSOR_TP_BOTH		0x3
+#define SENSOR_TP_ID(x)		(((x) & 0xff) << 4)
+	__le32 value_low;
+	__le32 value_high;
+} __packed;
+
+struct scmi_msg_sensor_reading_get {
+	__le32 id;
+	__le32 flags;
+#define SENSOR_READ_ASYNC	BIT(0)
+} __packed;
+
+struct scmi_sensors_info {
+	int num_sensors;
+	int max_requests;
+	u64 reg_addr;
+	u32 reg_size;
+};
+
+static struct scmi_sensors_info sensor_info;
+
+static int scmi_sensor_attributes_get(struct scmi_handle *handle,
+				      struct scmi_sensors_info *sensor_info)
+{
+	int ret;
+	struct scmi_xfer *t;
+	struct scmi_msg_resp_sensor_attributes *attr;
+
+	ret = scmi_one_xfer_init(handle, PROTOCOL_ATTRIBUTES,
+				 SCMI_PROTOCOL_SENSOR, 0, sizeof(*attr), &t);
+	if (ret)
+		return ret;
+
+	attr = (struct scmi_msg_resp_sensor_attributes *)t->rx.buf;
+
+	ret = scmi_do_xfer(handle, t);
+	if (!ret) {
+		sensor_info->num_sensors = le16_to_cpu(attr->num_sensors);
+		sensor_info->max_requests = le16_to_cpu(attr->max_requests);
+		sensor_info->reg_addr = le32_to_cpu(attr->reg_addr_low) |
+				(u64)le32_to_cpu(attr->reg_addr_high) << 32;
+		sensor_info->reg_size = le32_to_cpu(attr->reg_size);
+	}
+
+	scmi_put_one_xfer(handle, t);
+	return ret;
+}
+
+static int scmi_sensor_description_get(struct scmi_handle *handle)
+{
+	int ret, cnt;
+	u32 desc_index = 0;
+	u16 num_returned, num_remaining;
+	struct scmi_xfer *t;
+	struct scmi_msg_resp_sensor_description *sensors;
+
+	ret = scmi_one_xfer_init(handle, SENSOR_DESCRIPTION_GET,
+				 SCMI_PROTOCOL_SENSOR, sizeof(__le32), 0, &t);
+	if (ret)
+		return ret;
+
+	sensors = (struct scmi_msg_resp_sensor_description *)t->rx.buf;
+
+	do {
+		/* Set the number of sensors to be skipped/already read */
+		*(__le32 *)t->tx.buf = cpu_to_le32(desc_index);
+
+		ret = scmi_do_xfer(handle, t);
+		if (ret)
+			break;
+
+		num_returned = le16_to_cpu(sensors->num_returned);
+		num_remaining = le16_to_cpu(sensors->num_remaining);
+
+		if (desc_index + num_returned > sensor_info.num_sensors) {
+			dev_err(handle->dev, "No. of sensors can't exceed %d",
+				sensor_info.num_sensors);
+			break;
+		}
+
+		for (cnt = 0; cnt < num_returned; cnt++) {
+			dev_dbg(handle->dev, "Id %d, AttrH 0x%x AttrL 0x%x %s\n",
+				le32_to_cpu(sensors->desc[cnt].id),
+				le32_to_cpu(sensors->desc[cnt].attributes_high),
+				le32_to_cpu(sensors->desc[cnt].attributes_low),
+				sensors->desc[cnt].name);
+		}
+
+		desc_index += num_returned;
+		/*
+		 * check for both returned and remaining to avoid infinite
+		 * loop due to buggy firmware
+		 */
+	} while (num_returned && num_remaining);
+
+	scmi_put_one_xfer(handle, t);
+	return ret;
+}
+
+static int
+scmi_sensor_configuration_set(struct scmi_handle *handle, u32 sensor_id)
+{
+	int ret;
+	u32 evt_cntl = BIT(0);
+	struct scmi_xfer *t;
+	struct scmi_msg_set_sensor_config *cfg;
+
+	ret = scmi_one_xfer_init(handle, SENSOR_CONFIG_SET,
+				 SCMI_PROTOCOL_SENSOR, sizeof(*cfg), 0, &t);
+	if (ret)
+		return ret;
+
+	cfg = (struct scmi_msg_set_sensor_config *)t->tx.buf;
+	cfg->id = cpu_to_le32(sensor_id);
+	cfg->event_control = cpu_to_le32(evt_cntl);
+
+	ret = scmi_do_xfer(handle, t);
+
+	scmi_put_one_xfer(handle, t);
+	return ret;
+}
+
+static int scmi_sensor_trip_point_set(struct scmi_handle *handle, u32 sensor_id,
+				      u8 trip_id, u64 trip_value)
+{
+	int ret;
+	u32 evt_cntl = SENSOR_TP_BOTH;
+	struct scmi_xfer *t;
+	struct scmi_msg_set_sensor_trip_point *trip;
+
+	ret = scmi_one_xfer_init(handle, SENSOR_TRIP_POINT_SET,
+				 SCMI_PROTOCOL_SENSOR, sizeof(*trip), 0, &t);
+	if (ret)
+		return ret;
+
+	trip = (struct scmi_msg_set_sensor_trip_point *)t->tx.buf;
+	trip->id = cpu_to_le32(sensor_id);
+	trip->event_control = cpu_to_le32(evt_cntl | SENSOR_TP_ID(trip_id));
+	trip->value_low = cpu_to_le32(trip_value & 0xffffffff);
+	trip->value_high = cpu_to_le32(trip_value >> 32);
+
+	ret = scmi_do_xfer(handle, t);
+
+	scmi_put_one_xfer(handle, t);
+	return ret;
+}
+
+static int scmi_sensor_reading_get(struct scmi_handle *handle, u32 sensor_id,
+				   bool async, u64 *value)
+{
+	int ret;
+	struct scmi_xfer *t;
+	struct scmi_msg_sensor_reading_get *sensor;
+
+	ret = scmi_one_xfer_init(handle, SENSOR_READING_GET,
+				 SCMI_PROTOCOL_SENSOR, sizeof(*sensor),
+				 sizeof(u64), &t);
+	if (ret)
+		return ret;
+
+	sensor = (struct scmi_msg_sensor_reading_get *)t->tx.buf;
+	sensor->id = cpu_to_le32(sensor_id);
+	sensor->flags = cpu_to_le32(async ? SENSOR_READ_ASYNC : 0);
+
+	ret = scmi_do_xfer(handle, t);
+	if (!ret) {
+		__le32 *pval = (__le32 *)t->rx.buf;
+
+		*value = le32_to_cpu(*pval);
+		*value  |= le32_to_cpu(*(pval + 1));
+	}
+
+	scmi_put_one_xfer(handle, t);
+	return ret;
+}
+
+static struct scmi_sensor_ops sensor_ops = {
+	.configuration_set = scmi_sensor_configuration_set,
+	.trip_point_set = scmi_sensor_trip_point_set,
+	.reading_get = scmi_sensor_reading_get,
+};
+
+int scmi_sensors_protocol_init(struct scmi_handle *handle)
+{
+	u32 version;
+
+	if (!scmi_is_protocol_implemented(handle, SCMI_PROTOCOL_SENSOR)) {
+		dev_err(handle->dev, "SCMI Sensor protocol not implemented\n");
+		return -EPROTONOSUPPORT;
+	}
+
+	scmi_version_get(handle, SCMI_PROTOCOL_SENSOR, &version);
+
+	dev_dbg(handle->dev, "Sensor Version %d.%d\n",
+		PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
+
+	scmi_sensor_attributes_get(handle, &sensor_info);
+
+	scmi_sensor_description_get(handle);
+
+	handle->sensor_ops = &sensor_ops;
+
+	return 0;
+}
diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h
index 50a5816a295d..2b7836b4cb5a 100644
--- a/include/linux/scmi_protocol.h
+++ b/include/linux/scmi_protocol.h
@@ -99,6 +99,21 @@ struct scmi_power_ops {
 };
 
 /**
+ * struct scmi_sensor_ops - represents the various operations provided
+ *	by SCMI Sensor Protocol
+ *
+ * @configuration_set: control notifications on cross-over events for
+ *	the trip-points
+ * @trip_point_set: selects and configures a trip-point of interest
+ * @reading_get: gets the current value of the sensor
+ */
+struct scmi_sensor_ops {
+	int (*configuration_set)(struct scmi_handle *, u32);
+	int (*trip_point_set)(struct scmi_handle *, u32, u8, u64);
+	int (*reading_get)(struct scmi_handle *, u32, bool, u64 *);
+};
+
+/**
  * struct scmi_handle - Handle returned to ARM SCMI clients for usage.
  *
  * @dev: pointer to the SCMI device
@@ -106,6 +121,7 @@ struct scmi_power_ops {
  * @power_ops: pointer to set of power protocol operations
  * @perf_ops: pointer to set of performance protocol operations
  * @clk_ops: pointer to set of clock protocol operations
+ * @sensor_ops: pointer to set of sensor protocol operations
  */
 struct scmi_handle {
 	struct device *dev;
@@ -113,6 +129,7 @@ struct scmi_handle {
 	struct scmi_power_ops *power_ops;
 	struct scmi_perf_ops *perf_ops;
 	struct scmi_clk_ops *clk_ops;
+	struct scmi_sensor_ops *sensor_ops;
 };
 
 struct scmi_opp {
-- 
2.7.4

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

* [RFC PATCH 8/8] firmware: arm_scmi: probe and initialise all the supported protocols
  2017-06-07 16:10 [RFC PATCH 0/8] firmware: ARM System Control and Management Interface(SCMI) support Sudeep Holla
                   ` (6 preceding siblings ...)
  2017-06-07 16:10 ` [RFC PATCH 7/8] firmware: arm_scmi: add initial support for sensor protocol Sudeep Holla
@ 2017-06-07 16:10 ` Sudeep Holla
  7 siblings, 0 replies; 23+ messages in thread
From: Sudeep Holla @ 2017-06-07 16:10 UTC (permalink / raw)
  To: linux-kernel, devicetree
  Cc: Sudeep Holla, Roy Franz, Harb Abdulhamid, Nishanth Menon, Arnd Bergmann

Now that we have basic support for all the protocols in the
specification, let's probe them individually and initialise them.

Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
---
 drivers/firmware/arm_scmi/common.h |  5 +++++
 drivers/firmware/arm_scmi/driver.c | 30 +++++++++++++++++++++++++++++-
 2 files changed, 34 insertions(+), 1 deletion(-)

diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h
index 32873a315c76..e2ff799e7dd4 100644
--- a/drivers/firmware/arm_scmi/common.h
+++ b/drivers/firmware/arm_scmi/common.h
@@ -119,4 +119,9 @@ int scmi_version_get(struct scmi_handle *h, u8 protocol, u32 *version);
 bool scmi_is_protocol_implemented(struct scmi_handle *h, u8 prot_id);
 void scmi_setup_protocol_implemented(struct scmi_handle *handle, u8 *prot_imp);
 
+typedef int (*scmi_init_fn_t)(struct scmi_handle *);
 int scmi_base_protocol_init(struct scmi_handle *h);
+int scmi_perf_protocol_init(struct scmi_handle *h);
+int scmi_sensors_protocol_init(struct scmi_handle *h);
+int scmi_power_protocol_init(struct scmi_handle *h);
+int scmi_clock_protocol_init(struct scmi_handle *h);
diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
index 7b653c932edc..3300d0cce9e0 100644
--- a/drivers/firmware/arm_scmi/driver.c
+++ b/drivers/firmware/arm_scmi/driver.c
@@ -684,6 +684,23 @@ static int scmi_xfer_info_init(struct scmi_info *sinfo)
 	return 0;
 }
 
+static const struct of_device_id scmi_protocol_match[] = {
+	{
+		.compatible = "arm,scmi-perf-domains",
+		.data = scmi_perf_protocol_init,
+	}, {
+		.compatible = "arm,scmi-clocks",
+		.data = scmi_clock_protocol_init,
+	}, {
+		.compatible = "arm,scmi-power-domains",
+		.data = scmi_power_protocol_init,
+	}, {
+		.compatible = "arm,scmi-sensors",
+		.data = scmi_sensors_protocol_init,
+	},
+	{}
+};
+
 static int scmi_probe(struct platform_device *pdev)
 {
 	int ret = -EINVAL;
@@ -694,7 +711,7 @@ static int scmi_probe(struct platform_device *pdev)
 	const struct scmi_desc *desc;
 	struct scmi_info *info = NULL;
 	struct device *dev = &pdev->dev;
-	struct device_node *shmem, *np = dev->of_node;
+	struct device_node *child, *shmem, *np = dev->of_node;
 
 	desc = of_match_device(scmi_of_match, dev)->data;
 
@@ -755,6 +772,17 @@ static int scmi_probe(struct platform_device *pdev)
 		goto out;
 	}
 
+	for_each_available_child_of_node(np, child) {
+		scmi_init_fn_t fn;
+		const struct of_device_id *match;
+
+		match = of_match_node(scmi_protocol_match, child);
+		if (!match)
+			continue;
+		fn = match->data;
+		fn(handle);
+	}
+
 	mutex_lock(&scmi_list_mutex);
 	list_add_tail(&info->node, &scmi_list);
 	mutex_unlock(&scmi_list_mutex);
-- 
2.7.4

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

* Re: [RFC PATCH 2/8] firmware: arm_scmi: add basic driver infrastructure for SCMI
  2017-06-07 16:10 ` [RFC PATCH 2/8] firmware: arm_scmi: add basic driver infrastructure for SCMI Sudeep Holla
@ 2017-06-07 19:18   ` Roy Franz
  2017-06-08  9:28     ` Sudeep Holla
  0 siblings, 1 reply; 23+ messages in thread
From: Roy Franz @ 2017-06-07 19:18 UTC (permalink / raw)
  To: Sudeep Holla
  Cc: linux-kernel, devicetree, Roy Franz, Harb Abdulhamid,
	Nishanth Menon, Arnd Bergmann

On Wed, Jun 7, 2017 at 9:10 AM, Sudeep Holla <sudeep.holla@arm.com> wrote:
> The SCMI is intended to allow OSPM to manage various functions that are
> provided by the hardware platform it is running on, including power and
> performance functions. SCMI provides two levels of abstraction, protocols
> and transports. Protocols define individual groups of system control and
> management messages. A protocol specification describes the messages
> that it supports. Transports describe the method by which protocol
> messages are communicated between agents and the platform.
>
> This patch adds basic infrastructure to manage the message allocation,
> initialisation, packing/unpacking and shared memory management.
>
> Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
> ---
>  drivers/firmware/Kconfig           |  21 ++
>  drivers/firmware/Makefile          |   1 +
>  drivers/firmware/arm_scmi/Makefile |   2 +
>  drivers/firmware/arm_scmi/common.h |  74 ++++
>  drivers/firmware/arm_scmi/driver.c | 737 +++++++++++++++++++++++++++++++++++++
>  include/linux/scmi_protocol.h      |  48 +++
>  6 files changed, 883 insertions(+)
>  create mode 100644 drivers/firmware/arm_scmi/Makefile
>  create mode 100644 drivers/firmware/arm_scmi/common.h
>  create mode 100644 drivers/firmware/arm_scmi/driver.c
>  create mode 100644 include/linux/scmi_protocol.h
>
> diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
> index 6e4ed5a9c6fd..c3d1a12763ce 100644
> --- a/drivers/firmware/Kconfig
> +++ b/drivers/firmware/Kconfig
> @@ -19,6 +19,27 @@ config ARM_PSCI_CHECKER
>           on and off through hotplug, so for now torture tests and PSCI checker
>           are mutually exclusive.
>
> +config ARM_SCMI_PROTOCOL
> +       tristate "ARM System Control and Management Interface (SCMI) Message Protocol"
> +       depends on ARM || ARM64 || COMPILE_TEST
> +       depends on MAILBOX
> +       help
> +         ARM System Control and Management Interface (SCMI) protocol is a
> +         set of operating system-independent software interfaces that are
> +         used in system management. SCMI is extensible and currently provides
> +         interfaces for: Discovery and self-description of the interfaces
> +         it supports, Power domain management which is the ability to place
> +         a given device or domain into the various power-saving states that
> +         it supports, Performance management which is the ability to control
> +         the performance of a domain that is composed of compute engines
> +         such as application processors and other accelerators, Clock
> +         management which is the ability to set and inquire rates on platform
> +         managed clocks and Sensor management which is the ability to read
> +         sensor data, and be notified of sensor value.
> +
> +         This protocol library provides interface for all the client drivers
> +         making use of the features offered by the SCMI.
> +
>  config ARM_SCPI_PROTOCOL
>         tristate "ARM System Control and Power Interface (SCPI) Message Protocol"
>         depends on ARM || ARM64 || COMPILE_TEST
> diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile
> index a37f12e8d137..91d3ff62c653 100644
> --- a/drivers/firmware/Makefile
> +++ b/drivers/firmware/Makefile
> @@ -23,6 +23,7 @@ obj-$(CONFIG_QCOM_SCM_32)     += qcom_scm-32.o
>  CFLAGS_qcom_scm-32.o :=$(call as-instr,.arch armv7-a\n.arch_extension sec,-DREQUIRES_SEC=1) -march=armv7-a
>  obj-$(CONFIG_TI_SCI_PROTOCOL)  += ti_sci.o
>
> +obj-$(CONFIG_ARM_SCMI_PROTOCOL)        += arm_scmi/
>  obj-y                          += broadcom/
>  obj-y                          += meson/
>  obj-$(CONFIG_GOOGLE_FIRMWARE)  += google/
> diff --git a/drivers/firmware/arm_scmi/Makefile b/drivers/firmware/arm_scmi/Makefile
> new file mode 100644
> index 000000000000..58e94c95e523
> --- /dev/null
> +++ b/drivers/firmware/arm_scmi/Makefile
> @@ -0,0 +1,2 @@
> +obj-$(CONFIG_ARM_SCMI_PROTOCOL)        = arm_scmi.o
> +arm_scmi-y = driver.o
> diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h
> new file mode 100644
> index 000000000000..a3038efa3a8d
> --- /dev/null
> +++ b/drivers/firmware/arm_scmi/common.h
> @@ -0,0 +1,74 @@
> +/*
> + * System Control and Management Interface (SCMI) Message Protocol
> + * driver common header file containing some definitions, structures
> + * and function prototypes used in all the different SCMI protocols.
> + *
> + * Copyright (C) 2017 ARM Ltd.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
> + * more details.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <linux/completion.h>
> +#include <linux/scmi_protocol.h>
> +#include <linux/types.h>
> +
> +/**
> + * struct scmi_msg_hdr - Message(Tx/Rx) header
> + *
> + * @id: The identifier of the command being sent
> + * @protocol_id: The identifier of the protocol used to send @id command
> + * @seq: The token to identify the message. when a message/command returns,
> + *       the platform returns the whole message header unmodified including
> + *      the token.
> + */
> +struct scmi_msg_hdr {
> +       u8 id;
> +       u8 protocol_id;
> +       u16 seq;
> +       u32 status;
> +       bool poll_completion;
> +};
> +
> +/**
> + * struct scmi_msg - Message(Tx/Rx) structure
> + *
> + * @len: Length of data in the Buffer
> + * @buf: Buffer pointer
> + */
> +struct scmi_msg {
> +       u8 *buf;
> +       size_t len;
> +};
> +
> +/**
> + * struct scmi_xfer - Structure representing a message flow
> + *
> + * @hdr: Transmit message header
> + * @tx: Transmit message
> + * @rx: Receive message, the buffer should be pre-allocated to store
> + *     message. If request-ACK protocol is used, we can reuse the same
> + *     buffer for the rx path as we use for the tx path.
> + * @done: completion event
> + */
> +
> +struct scmi_xfer {
> +       struct scmi_msg_hdr hdr;
> +       struct scmi_msg tx;
> +       struct scmi_msg rx;
> +       struct completion done;
> +};
> +
> +void scmi_put_one_xfer(struct scmi_handle *h, struct scmi_xfer *xfer);
> +int scmi_do_xfer(struct scmi_handle *h, struct scmi_xfer *xfer);
> +int scmi_one_xfer_init(struct scmi_handle *h, u8 msg_id, u8 msg_prot_id,
> +                      size_t tx_size, size_t rx_size, struct scmi_xfer **p);
> diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
> new file mode 100644
> index 000000000000..f01e0643ac7d
> --- /dev/null
> +++ b/drivers/firmware/arm_scmi/driver.c
> @@ -0,0 +1,737 @@
> +/*
> + * System Control and Management Interface (SCMI) Message Protocol driver
> + *
> + * SCMI Message Protocol is used between the System Control Processor(SCP)
> + * and the Application Processors(AP). The Message Handling Unit(MHU)
> + * provides a mechanism for inter-processor communication between SCP's
> + * Cortex M3 and AP.
> + *
> + * SCP offers control and management of the core/cluster power states,
> + * various power domain DVFS including the core/cluster, certain system
> + * clocks configuration, thermal sensors and many others.
> + *
> + * Copyright (C) 2017 ARM Ltd.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
> + * more details.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <linux/bitmap.h>
> +#include <linux/export.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/mailbox_client.h>
> +#include <linux/module.h>
> +#include <linux/of_address.h>
> +#include <linux/of_device.h>
> +#include <linux/semaphore.h>
> +#include <linux/slab.h>
> +
> +#include "common.h"
> +
> +#define MSG_ID_SHIFT           0
> +#define MSG_ID_MASK            0xff
> +#define MSG_TYPE_SHIFT         8
> +#define MSG_TYPE_MASK          0x3
> +#define MSG_PROTOCOL_ID_SHIFT  10
> +#define MSG_PROTOCOL_ID_MASK   0xff
> +#define MSG_TOKEN_ID_SHIFT     18
> +#define MSG_TOKEN_ID_MASK      0x3ff
> +#define MSG_XTRACT_TOKEN(header)       \
> +       (((header) >> MSG_TOKEN_ID_SHIFT) & MSG_TOKEN_ID_MASK)
> +
> +enum scmi_error_codes {
> +       SCMI_SUCCESS = 0,       /* Success */
> +       SCMI_ERR_SUPPORT = -1,  /* Not supported */
> +       SCMI_ERR_PARAMS = -2,   /* Invalid Parameters */
> +       SCMI_ERR_ACCESS = -3,   /* Invalid access/permission denied */
> +       SCMI_ERR_ENTRY = -4,    /* Not found */
> +       SCMI_ERR_RANGE = -5,    /* Value out of range */
> +       SCMI_ERR_BUSY = -6,     /* Device busy */
> +       SCMI_ERR_COMMS = -7,    /* Communication Error */
> +       SCMI_ERR_GENERIC = -8,  /* Generic Error */
> +       SCMI_ERR_HARDWARE = -9, /* Hardware Error */
> +       SCMI_ERR_PROTOCOL = -10,/* Protocol Error */
> +       SCMI_ERR_MAX
> +};
> +
> +/* List of all  SCMI devices active in system */
> +static LIST_HEAD(scmi_list);
> +/* Protection for the entire list */
> +static DEFINE_MUTEX(scmi_list_mutex);
> +
> +/**
> + * struct scmi_xfers_info - Structure to manage transfer information
> + *
> + * @sem_xfer_count: Counting Semaphore for managing max simultaneous
> + *     Messages.
> + * @xfer_block: Preallocated Message array
> + * @xfer_alloc_table: Bitmap table for allocated messages.
> + *     Index of this bitmap table is also used for message
> + *     sequence identifier.
> + * @xfer_lock: Protection for message allocation
> + */
> +struct scmi_xfers_info {
> +       struct semaphore sem_xfer_count;
> +       struct scmi_xfer *xfer_block;
> +       unsigned long *xfer_alloc_table;
> +       /* protect transfer allocation */
> +       spinlock_t xfer_lock;
> +};
> +
> +/**
> + * struct scmi_desc - Description of SoC integration
> + *
> + * @max_rx_timeout_ms: Timeout for communication with SoC (in Milliseconds)
> + * @max_msg: Maximum number of messages that can be pending
> + *     simultaneously in the system
> + * @max_msg_size: Maximum size of data per message that can be handled.
> + */
> +struct scmi_desc {
> +       int max_rx_timeout_ms;
> +       int max_msg;
> +       int max_msg_size;
> +};
> +
> +/**
> + * struct scmi_info - Structure representing a  SCMI instance
> + *
> + * @dev: Device pointer
> + * @desc: SoC description for this instance
> + * @handle: Instance of SCMI handle to send to clients
> + * @cl: Mailbox Client
> + * @tx_chan: Transmit mailbox channel
> + * @rx_chan: Receive mailbox channel
> + * @tx_payload: Transmit mailbox channel payload area
> + * @rx_payload: Receive mailbox channel payload area
> + * @minfo: Message info
> + * @node: list head
> + * @users: Number of users of this instance
> + */
> +struct scmi_info {
> +       struct device *dev;
> +       const struct scmi_desc *desc;
> +       struct scmi_handle handle;
> +       struct mbox_client cl;
> +       struct mbox_chan *tx_chan;
> +       struct mbox_chan *rx_chan;
> +       void __iomem *tx_payload;
> +       void __iomem *rx_payload;
> +       struct scmi_xfers_info minfo;
> +       struct list_head node;
> +       int users;
> +};
> +
> +#define client_to_scmi_info(c) container_of(c, struct scmi_info, cl)
> +#define handle_to_scmi_info(h) container_of(h, struct scmi_info, handle)
> +
> +/*
> + * The SCP firmware only executes in little-endian mode, so any buffers
> + * shared through SCMI should have their contents converted to little-endian
> + */

nit:
This really has more to do with the SCMI protocol defining everything
as little endian,
rather the endian-ness of the SCP, right?  There could be SCP
implementations that
are not Cortex M3s or little endian.

> +struct scmi_shared_mem {
> +       __le32 reserved;
> +       __le32 channel_status;
> +#define SCMI_SHMEM_CHAN_STAT_CHANNEL_ERROR     BIT(1)
> +#define SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE      BIT(0)
> +       __le32 reserved1[2];
> +       __le32 flags;
> +#define SCMI_SHMEM_FLAG_INTR_ENABLED   BIT(0)
> +       __le32 length;
> +       __le32 msg_header;
> +       u8 msg_payload[0];
> +} __packed;
> +
> +static int scmi_linux_errmap[] = {
> +       /* better than switch case as long as return value is continuous */
> +       0,                      /* SCMI_SUCCESS */
> +       -EOPNOTSUPP,            /* SCMI_ERR_SUPPORT */
> +       -EINVAL,                /* SCMI_ERR_PARAM */
> +       -EACCES,                /* SCMI_ERR_ACCESS */
> +       -ENOENT,                /* SCMI_ERR_ENTRY */
> +       -ERANGE,                /* SCMI_ERR_RANGE */
> +       -EBUSY,                 /* SCMI_ERR_BUSY */
> +       -ECOMM,                 /* SCMI_ERR_COMMS */
> +       -EIO,                   /* SCMI_ERR_GENERIC */
> +       -EREMOTEIO,             /* SCMI_ERR_HARDWARE */
> +       -EPROTO,                /* SCMI_ERR_PROTOCOL */
> +};
> +
> +static inline int scmi_to_linux_errno(int errno)
> +{
> +       if (errno < SCMI_SUCCESS && errno > SCMI_ERR_MAX)
> +               return scmi_linux_errmap[-errno];
> +       return -EIO;
> +}
> +
> +/**
> + * scmi_dump_header_dbg() - Helper to dump a message header.
> + *
> + * @dev: Device pointer corresponding to the SCMI entity
> + * @hdr: pointer to header.
> + */
> +static inline void scmi_dump_header_dbg(struct device *dev,
> +                                       struct scmi_msg_hdr *hdr)
> +{
> +       dev_dbg(dev, "Command ID: %x Sequence ID: %x Protocol: %x\n",
> +               hdr->id, hdr->seq, hdr->protocol_id);
> +}
> +
> +/**
> + * scmi_rx_callback() - mailbox client callback for receive messages
> + *
> + * @cl: client pointer
> + * @m: mailbox message
> + *
> + * Processes one received message to appropriate transfer information and
> + * signals completion of the transfer.
> + *
> + * NOTE: This function will be invoked in IRQ context, hence should be
> + * as optimal as possible.
> + */
> +static void scmi_rx_callback(struct mbox_client *cl, void *m)
> +{
> +       u16 xfer_id;
> +       struct scmi_xfer *xfer;
> +       struct scmi_info *info = client_to_scmi_info(cl);
> +       struct scmi_xfers_info *minfo = &info->minfo;
> +       struct device *dev = info->dev;
> +       struct scmi_shared_mem *mem = info->tx_payload;
> +
> +       xfer_id = MSG_XTRACT_TOKEN(mem->msg_header);
> +
> +       /*
> +        * Are we even expecting this?
> +        */
> +       if (!test_bit(xfer_id, minfo->xfer_alloc_table)) {
> +               dev_err(dev, "message for %d is not expected!\n", xfer_id);
> +               return;
> +       }
> +
> +       xfer = &minfo->xfer_block[xfer_id];
> +
> +       scmi_dump_header_dbg(dev, &xfer->hdr);
> +       /* Is the message of valid length? */
> +       if (xfer->rx.len > info->desc->max_msg_size) {
> +               dev_err(dev, "unable to handle %lu xfer(max %d)\n",
> +                       xfer->rx.len, info->desc->max_msg_size);
> +               return;
> +       }
> +
> +       xfer->hdr.status = le32_to_cpu(*(__le32 *)mem->msg_payload);
> +       /* Skip the length of header and statues in payload area i.e 8 bytes*/
> +       xfer->rx.len = min_t(size_t, xfer->rx.len, mem->length - 8);
> +
> +       /* Take a copy to the rx buffer.. */
> +       memcpy_fromio(xfer->rx.buf, mem->msg_payload + 4, xfer->rx.len);
> +       complete(&xfer->done);
> +}
> +
> +/**
> + * pack_scmi_header() - packs and returns 32-bit header
> + *
> + * @hdr: pointer to header containing all the information on message id,
> + *     protocol id and sequence id.
> + */
> +static inline u32 pack_scmi_header(struct scmi_msg_hdr *hdr)
> +{
> +       return ((hdr->id & MSG_ID_MASK) << MSG_ID_SHIFT) |
> +          ((hdr->seq & MSG_TOKEN_ID_MASK) << MSG_TOKEN_ID_SHIFT) |
> +          ((hdr->protocol_id & MSG_PROTOCOL_ID_MASK) << MSG_PROTOCOL_ID_SHIFT);
> +}
> +
> +/**
> + * scmi_tx_prepare() - mailbox client callback to prepare for the transfer
> + *
> + * @cl: client pointer
> + * @m: mailbox message
> + *
> + * This function prepares the shared memory which contains the header and the
> + * payload.
> + */
> +static void scmi_tx_prepare(struct mbox_client *cl, void *m)
> +{
> +       struct scmi_xfer *t = m;
> +       struct scmi_info *info = client_to_scmi_info(cl);
> +       struct scmi_shared_mem *mem = info->tx_payload;
> +
> +       mem->channel_status = 0x0; /* Mark channel busy + clear error */
> +       mem->flags = t->hdr.poll_completion ? 0 : SCMI_SHMEM_FLAG_INTR_ENABLED;
> +       mem->length = sizeof(mem->msg_header) + t->tx.len;
> +       mem->msg_header = cpu_to_le32(pack_scmi_header(&t->hdr));
> +       if (t->tx.buf)
> +               memcpy_toio(mem->msg_payload, t->tx.buf, t->tx.len);
> +}
> +
> +/**
> + * scmi_one_xfer_get() - Allocate one message
> + *
> + * @handle: SCMI entity handle
> + *
> + * Helper function which is used by various command functions that are
> + * exposed to clients of this driver for allocating a message traffic event.
> + *
> + * This function can sleep depending on pending requests already in the system
> + * for the SCMI entity. Further, this also holds a spinlock to maintain
> + * integrity of internal data structures.
> + *
> + * Return: 0 if all went fine, else corresponding error.
> + */
> +static struct scmi_xfer *scmi_one_xfer_get(struct scmi_handle *handle)
> +{
> +       u16 xfer_id;
> +       int ret, timeout;
> +       struct scmi_xfer *xfer;
> +       unsigned long flags, bit_pos;
> +       struct scmi_info *info = handle_to_scmi_info(handle);
> +       struct scmi_xfers_info *minfo = &info->minfo;
> +
> +       /*
> +        * Ensure we have only controlled number of pending messages.
> +        * Ideally, we might just have to wait a single message, be
> +        * conservative and wait 5 times that..
> +        */
> +       timeout = msecs_to_jiffies(info->desc->max_rx_timeout_ms) * 5;
> +       ret = down_timeout(&minfo->sem_xfer_count, timeout);
> +       if (ret < 0)
> +               return ERR_PTR(ret);
> +
> +       /* Keep the locked section as small as possible */
> +       spin_lock_irqsave(&minfo->xfer_lock, flags);
> +       bit_pos = find_first_zero_bit(minfo->xfer_alloc_table,
> +                                     info->desc->max_msg);
> +       set_bit(bit_pos, minfo->xfer_alloc_table);
> +       spin_unlock_irqrestore(&minfo->xfer_lock, flags);
> +
> +       xfer_id = bit_pos;
> +
> +       xfer = &minfo->xfer_block[xfer_id];
> +       xfer->hdr.seq = xfer_id;
> +       reinit_completion(&xfer->done);
> +
> +       return xfer;
> +}
> +
> +/**
> + * scmi_put_one_xfer() - Release a message
> + *
> + * @minfo: transfer info pointer
> + * @xfer: message that was reserved by scmi_one_xfer_get
> + *
> + * This holds a spinlock to maintain integrity of internal data structures.
> + */
> +void scmi_put_one_xfer(struct scmi_handle *handle, struct scmi_xfer *xfer)
> +{
> +       u16 xfer_id;
> +       unsigned long flags;
> +       struct scmi_msg_hdr *hdr;
> +       struct scmi_info *info = handle_to_scmi_info(handle);
> +       struct scmi_xfers_info *minfo = &info->minfo;
> +
> +       hdr = (struct scmi_msg_hdr *)xfer->tx.buf;
> +       xfer_id = hdr->seq;
> +
> +       /*
> +        * Keep the locked section as small as possible
> +        * NOTE: we might escape with smp_mb and no lock here..
> +        * but just be conservative and symmetric.
> +        */
> +       spin_lock_irqsave(&minfo->xfer_lock, flags);
> +       clear_bit(xfer_id, minfo->xfer_alloc_table);
> +       spin_unlock_irqrestore(&minfo->xfer_lock, flags);
> +
> +       /* Increment the count for the next user to get through */
> +       up(&minfo->sem_xfer_count);
> +}
> +
> +/**
> + * scmi_do_xfer() - Do one transfer
> + *
> + * @info: Pointer to SCMI entity information
> + * @xfer: Transfer to initiate and wait for response
> + *
> + * Return: -ETIMEDOUT in case of no response, if transmit error,
> + *   return corresponding error, else if all goes well,
> + *   return 0.
> + */
> +int scmi_do_xfer(struct scmi_handle *handle, struct scmi_xfer *xfer)
> +{
> +       int ret;
> +       int timeout;
> +       struct scmi_info *info = handle_to_scmi_info(handle);
> +       struct device *dev = info->dev;
> +
> +       ret = mbox_send_message(info->tx_chan, xfer);
> +       if (ret < 0) {
> +               dev_dbg(dev, "mbox send fail %d\n", ret);
> +               return ret;
> +       }
> +
> +       /* mbox_send_message returns non-negative value on success, so reset */
> +       ret = 0;
> +
> +       /* And we wait for the response. */
> +       timeout = msecs_to_jiffies(info->desc->max_rx_timeout_ms);
> +       if (!wait_for_completion_timeout(&xfer->done, timeout)) {
> +               dev_err(dev, "mbox timed out in resp(caller: %pF)\n",
> +                       (void *)_RET_IP_);
> +               ret = -ETIMEDOUT;
> +       } else if (xfer->hdr.status) {
> +               ret = scmi_to_linux_errno(xfer->hdr.status);
> +       }
> +       /*
> +        * NOTE: we might prefer not to need the mailbox ticker to manage the
> +        * transfer queueing since the protocol layer queues things by itself.
> +        * Unfortunately, we have to kick the mailbox framework after we have
> +        * received our message.
> +        */
> +       mbox_client_txdone(info->tx_chan, ret);
> +
> +       return ret;
> +}
> +
> +/**
> + * scmi_one_xfer_init() - Allocate and initialise one message
> + *
> + * @handle: SCMI entity handle
> + * @msg_id: Message identifier
> + * @msg_prot_id: Protocol identifier for the message
> + * @tx_size: transmit message size
> + * @rx_size: receive message size
> + * @p: pointer to the allocated and initialised message
> + *
> + * This function allocates the message using @scmi_one_xfer_get and
> + * initialise the header.
> + *
> + * Return: 0 if all went fine with @p pointing to message, else
> + *     corresponding error.
> + */
> +int scmi_one_xfer_init(struct scmi_handle *handle, u8 msg_id, u8 msg_prot_id,
> +                      size_t tx_size, size_t rx_size, struct scmi_xfer **p)
> +{
> +       int ret;
> +       struct scmi_xfer *xfer;
> +       struct scmi_info *info = handle_to_scmi_info(handle);
> +       struct device *dev = info->dev;
> +
> +       /* Ensure we have sane transfer sizes */
> +       if (rx_size > info->desc->max_msg_size ||
> +           tx_size > info->desc->max_msg_size)
> +               return -ERANGE;
> +
> +       xfer = scmi_one_xfer_get(handle);
> +       if (IS_ERR(xfer)) {
> +               ret = PTR_ERR(xfer);
> +               dev_err(dev, "failed to get free message slot(%d)\n", ret);
> +               return ret;
> +       }
> +
> +       xfer->tx.len = tx_size;
> +       xfer->rx.len = rx_size ? : info->desc->max_msg_size;
> +       xfer->hdr.id = msg_id;
> +       xfer->hdr.protocol_id = msg_prot_id;
> +
> +       *p = xfer;
> +       return 0;
> +}
> +
> +/**
> + * scmi_handle_get() - Get the  SCMI handle for a device
> + *
> + * @dev: pointer to device for which we want SCMI handle
> + *
> + * NOTE: The function does not track individual clients of the framework
> + * and is expected to be maintained by caller of  SCMI protocol library.
> + * scmi_put_handle must be balanced with successful scmi_handle_get
> + *
> + * Return: pointer to handle if successful, else:
> + *     -EPROBE_DEFER if the instance is not ready
> + *     -ENODEV if the required node handler is missing
> + *     -EINVAL if invalid conditions are encountered.
> + */
> +const struct scmi_handle *scmi_handle_get(struct device *dev)
> +{
> +       struct list_head *p;
> +       struct scmi_info *info;
> +       struct device_node *scmi_np;
> +       struct scmi_handle *handle = NULL;
> +
> +       if (!dev) {
> +               pr_err("missing device pointer\n");
> +               return ERR_PTR(-EINVAL);
> +       }
> +       scmi_np = of_get_parent(dev->of_node);
> +       if (!scmi_np) {
> +               dev_err(dev, "no OF information\n");
> +               return ERR_PTR(-EINVAL);
> +       }
> +
> +       mutex_lock(&scmi_list_mutex);
> +       list_for_each(p, &scmi_list) {
> +               info = list_entry(p, struct scmi_info, node);
> +               if (scmi_np == info->dev->of_node) {
> +                       handle = &info->handle;
> +                       info->users++;
> +                       break;
> +               }
> +       }
> +       mutex_unlock(&scmi_list_mutex);
> +       of_node_put(scmi_np);
> +
> +       if (!handle)
> +               return ERR_PTR(-EPROBE_DEFER);
> +
> +       return handle;
> +}
> +EXPORT_SYMBOL_GPL(scmi_handle_get);
> +
> +/**
> + * scmi_put_handle() - Release the handle acquired by scmi_handle_get
> + *
> + * @handle: handle acquired by scmi_handle_get
> + *
> + * NOTE: The function does not track individual clients of the framework
> + * and is expected to be maintained by caller of  SCMI protocol library.
> + * scmi_put_handle must be balanced with successful scmi_handle_get
> + *
> + * Return: 0 is successfully released
> + *     if an error pointer was passed, it returns the error value back,
> + *     if null was passed, it returns -EINVAL;
> + */
> +int scmi_put_handle(const struct scmi_handle *handle)
> +{
> +       struct scmi_info *info;
> +
> +       if (IS_ERR(handle))
> +               return PTR_ERR(handle);
> +       if (!handle)
> +               return -EINVAL;
> +
> +       info = handle_to_scmi_info(handle);
> +       mutex_lock(&scmi_list_mutex);
> +       if (!WARN_ON(!info->users))
> +               info->users--;
> +       mutex_unlock(&scmi_list_mutex);
> +
> +       return 0;
> +}
> +EXPORT_SYMBOL_GPL(scmi_put_handle);
> +
> +static void devm_scmi_release(struct device *dev, void *res)
> +{
> +       const struct scmi_handle **ptr = res;
> +       const struct scmi_handle *handle = *ptr;
> +       int ret;
> +
> +       ret = scmi_put_handle(handle);
> +       if (ret)
> +               dev_err(dev, "failed to put handle %d\n", ret);
> +}
> +
> +/**
> + * devm_scmi_handle_get() - Managed get handle
> + * @dev: device for which we want SCMI handle for.
> + *
> + * NOTE: This releases the handle once the device resources are
> + * no longer needed. MUST NOT BE released with scmi_put_handle.
> + * The function does not track individual clients of the framework
> + * and is expected to be maintained by caller of  SCMI protocol library.
> + *
> + * Return: 0 if all went fine, else corresponding error.
> + */
> +const struct scmi_handle *devm_scmi_handle_get(struct device *dev)
> +{
> +       const struct scmi_handle **ptr;
> +       const struct scmi_handle *handle;
> +
> +       ptr = devres_alloc(devm_scmi_release, sizeof(*ptr), GFP_KERNEL);
> +       if (!ptr)
> +               return ERR_PTR(-ENOMEM);
> +       handle = scmi_handle_get(dev);
> +
> +       if (!IS_ERR(handle)) {
> +               *ptr = handle;
> +               devres_add(dev, ptr);
> +       } else {
> +               devres_free(ptr);
> +       }
> +
> +       return handle;
> +}
> +EXPORT_SYMBOL_GPL(devm_scmi_handle_get);
> +
> +static const struct scmi_desc scmi_generic_desc = {
> +       .max_rx_timeout_ms = 30,        /* we may increase this if required */
> +       .max_msg = 20,          /* Limited by MBOX_TX_QUEUE_LEN */
> +       .max_msg_size = 128,
> +};
> +
> +/* Each compatible listed below must have descriptor associated with it */
> +static const struct of_device_id scmi_of_match[] = {
> +       { .compatible = "arm,scmi", .data = &scmi_generic_desc },
> +       { /* Sentinel */ },
> +};
> +
> +MODULE_DEVICE_TABLE(of, scmi_of_match);
> +
> +static int scmi_xfer_info_init(struct scmi_info *sinfo)
> +{
> +       int i;
> +       struct scmi_xfer *xfer;
> +       struct device *dev = sinfo->dev;
> +       const struct scmi_desc *desc = sinfo->desc;
> +       struct scmi_xfers_info *info = &sinfo->minfo;
> +
> +       /* Pre-allocated messages, no more than what hdr.seq can support */
> +       if (WARN_ON(desc->max_msg >= (MSG_TOKEN_ID_MASK + 1))) {
> +               dev_err(dev, "Maximum message of %d exceeds supported %d\n",
> +                       desc->max_msg, MSG_TOKEN_ID_MASK + 1);
> +               return -EINVAL;
> +       }
> +
> +       info->xfer_block = devm_kcalloc(dev, desc->max_msg,
> +                                       sizeof(*info->xfer_block), GFP_KERNEL);
> +       if (!info->xfer_block)
> +               return -ENOMEM;
> +
> +       info->xfer_alloc_table = devm_kcalloc(dev, BITS_TO_LONGS(desc->max_msg),
> +                                             sizeof(long), GFP_KERNEL);
> +       if (!info->xfer_alloc_table)
> +               return -ENOMEM;
> +
> +       bitmap_zero(info->xfer_alloc_table, desc->max_msg);
> +
> +       /* Pre-initialize the buffer pointer to pre-allocated buffers */
> +       for (i = 0, xfer = info->xfer_block; i < desc->max_msg; i++, xfer++) {
> +               xfer->rx.buf = devm_kcalloc(dev, sizeof(*xfer->rx.buf),
> +                                           desc->max_msg_size, GFP_KERNEL);
> +               if (!xfer->rx.buf)
> +                       return -ENOMEM;
> +
> +               xfer->tx.buf = xfer->rx.buf;
> +               init_completion(&xfer->done);
> +       }
> +
> +       spin_lock_init(&info->xfer_lock);
> +
> +       sema_init(&info->sem_xfer_count, desc->max_msg);
> +
> +       return 0;
> +}
> +
> +static int scmi_probe(struct platform_device *pdev)
> +{
> +       int ret = -EINVAL;
> +       struct resource res;
> +       resource_size_t size;
> +       struct mbox_client *cl;
> +       struct scmi_handle *handle;
> +       const struct scmi_desc *desc;
> +       struct scmi_info *info = NULL;
> +       struct device *dev = &pdev->dev;
> +       struct device_node *shmem, *np = dev->of_node;
> +
> +       desc = of_match_device(scmi_of_match, dev)->data;
> +
> +       info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
> +       if (!info)
> +               return -ENOMEM;
> +
> +       info->dev = dev;
> +       info->desc = desc;
> +       INIT_LIST_HEAD(&info->node);
> +
> +       ret = scmi_xfer_info_init(info);
> +       if (ret)
> +               return ret;
> +
> +       platform_set_drvdata(pdev, info);
> +
> +       cl = &info->cl;
> +       cl->dev = dev;
> +       cl->rx_callback = scmi_rx_callback;
> +       cl->tx_prepare = scmi_tx_prepare;
> +       cl->tx_block = false;
> +       cl->knows_txdone = true;
> +
> +       shmem = of_parse_phandle(np, "shmem", 0);
> +       ret = of_address_to_resource(shmem, 0, &res);
> +       of_node_put(shmem);
> +       if (ret) {
> +               dev_err(dev, "failed to get SCMI Tx payload mem resource\n");
> +               return ret;
> +       }
> +
> +       size = resource_size(&res);
> +       info->tx_payload = devm_ioremap(dev, res.start, size);
> +       if (!info->tx_payload) {
> +               dev_err(dev, "failed to ioremap SCMI Tx payload\n");
> +               ret = -EADDRNOTAVAIL;
> +               return ret;
> +       }
> +
> +       info->tx_chan = mbox_request_channel_byname(cl, "tx");
> +       if (IS_ERR(info->tx_chan)) {
> +               ret = PTR_ERR(info->tx_chan);
> +               goto out;
> +       }
> +
> +       handle = &info->handle;
> +       handle->dev = info->dev;
> +
> +       mutex_lock(&scmi_list_mutex);
> +       list_add_tail(&info->node, &scmi_list);
> +       mutex_unlock(&scmi_list_mutex);
> +
> +       return of_platform_populate(dev->of_node, NULL, NULL, dev);
> +out:
> +       if (!IS_ERR(info->tx_chan))
> +               mbox_free_channel(info->tx_chan);
> +       return ret;
> +}
> +
> +static int scmi_remove(struct platform_device *pdev)
> +{
> +       int ret = 0;
> +       struct scmi_info *info = platform_get_drvdata(pdev);
> +
> +       of_platform_depopulate(&pdev->dev);
> +
> +       mutex_lock(&scmi_list_mutex);
> +       if (info->users)
> +               ret = -EBUSY;
> +       else
> +               list_del(&info->node);
> +       mutex_unlock(&scmi_list_mutex);
> +
> +       if (!ret)
> +               /* Safe to free channels since no more users */
> +               mbox_free_channel(info->tx_chan);
> +
> +       return ret;
> +}
> +
> +static struct platform_driver scmi_driver = {
> +       .driver = {
> +                  .name = "arm-scmi",
> +                  .of_match_table = of_match_ptr(scmi_of_match),
> +                  },
> +       .probe = scmi_probe,
> +       .remove = scmi_remove,
> +};
> +
> +module_platform_driver(scmi_driver);
> +
> +MODULE_ALIAS("platform: arm-scmi");
> +MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");
> +MODULE_DESCRIPTION("ARM SCMI protocol driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h
> new file mode 100644
> index 000000000000..0c795a765110
> --- /dev/null
> +++ b/include/linux/scmi_protocol.h
> @@ -0,0 +1,48 @@
> +/*
> + * SCMI Message Protocol driver header
> + *
> + * Copyright (C) 2017 ARM Ltd.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
> + * more details.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +#include <linux/types.h>
> +
> +/**
> + * struct scmi_handle - Handle returned to ARM SCMI clients for usage.
> + *
> + * @dev: pointer to the SCMI device
> + */
> +struct scmi_handle {
> +       struct device *dev;
> +};
> +
> +#if IS_REACHABLE(CONFIG_ARM_SCMI_PROTOCOL)
> +int scmi_put_handle(const struct scmi_handle *handle);
> +const struct scmi_handle *scmi_handle_get(struct device *dev);
> +const struct scmi_handle *devm_scmi_handle_get(struct device *dev);
> +#else
> +static inline int scmi_put_handle(const struct scmi_handle *handle)
> +{
> +       return 0;
> +}
> +
> +static inline const struct scmi_handle *scmi_handle_get(struct device *dev)
> +{
> +       return NULL;
> +}
> +
> +static inline const struct scmi_handle *devm_scmi_handle_get(struct device *dev)
> +{
> +       return NULL;
> +}
> +#endif /* CONFIG_ARM_SCMI_PROTOCOL */
> --
> 2.7.4
>

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

* Re: [RFC PATCH 3/8] firmware: arm_scmi: add common infrastructure and support for base protocol
  2017-06-07 16:10 ` [RFC PATCH 3/8] firmware: arm_scmi: add common infrastructure and support for base protocol Sudeep Holla
@ 2017-06-07 19:19   ` Roy Franz
  0 siblings, 0 replies; 23+ messages in thread
From: Roy Franz @ 2017-06-07 19:19 UTC (permalink / raw)
  To: Sudeep Holla
  Cc: linux-kernel, devicetree, Roy Franz, Harb Abdulhamid,
	Nishanth Menon, Arnd Bergmann

On Wed, Jun 7, 2017 at 9:10 AM, Sudeep Holla <sudeep.holla@arm.com> wrote:
> The base protocol describes the properties of the implementation and
> provide generic error management. The base protocol provides commands
> to describe protocol version, discover implementation specific
> attributes and vendor/sub-vendor identification, list of protocols
> implemented and the various agents are in the system including OSPM
> and the platform. It also supports registering for notifications of
> platform errors.
>
> This protocol is mandatory. This patch adds support for the same along
> with some basic infrastructure to add support for other protocols.
>
> Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
> ---
>  drivers/firmware/arm_scmi/Makefile |   2 +-
>  drivers/firmware/arm_scmi/base.c   | 290 +++++++++++++++++++++++++++++++++++++
>  drivers/firmware/arm_scmi/common.h |  46 ++++++
>  drivers/firmware/arm_scmi/driver.c |  67 +++++++++
>  include/linux/scmi_protocol.h      |  28 ++++
>  5 files changed, 432 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/firmware/arm_scmi/base.c
>
> diff --git a/drivers/firmware/arm_scmi/Makefile b/drivers/firmware/arm_scmi/Makefile
> index 58e94c95e523..21d01d1d6b9c 100644
> --- a/drivers/firmware/arm_scmi/Makefile
> +++ b/drivers/firmware/arm_scmi/Makefile
> @@ -1,2 +1,2 @@
>  obj-$(CONFIG_ARM_SCMI_PROTOCOL)        = arm_scmi.o
> -arm_scmi-y = driver.o
> +arm_scmi-y = base.o driver.o
> diff --git a/drivers/firmware/arm_scmi/base.c b/drivers/firmware/arm_scmi/base.c
> new file mode 100644
> index 000000000000..1191a409ea73
> --- /dev/null
> +++ b/drivers/firmware/arm_scmi/base.c
> @@ -0,0 +1,290 @@
> +/*
> + * System Control and Management Interface (SCMI) Base Protocol
> + *
> + * Copyright (C) 2017 ARM Ltd.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
> + * more details.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include "common.h"
> +
> +enum scmi_base_protocol_cmd {
> +       BASE_DISCOVER_VENDOR = 0x3,
> +       BASE_DISCOVER_SUB_VENDOR = 0x4,
> +       BASE_DISCOVER_IMPLEMENT_VERSION = 0x5,
> +       BASE_DISCOVER_LIST_PROTOCOLS = 0x6,
> +       BASE_DISCOVER_AGENT = 0x7,
> +       BASE_NOTIFY_ERRORS = 0x8,
> +};
> +
> +struct scmi_msg_resp_base_attributes {
> +           u8 num_protocols;
> +           u8 num_agents;
> +       __le16 reserved;
> +} __packed;
> +
> +/**
> + * scmi_base_attributes_get() - gets the implementation details
> + *     that are associated with the base protocol.
> + *
> + * @handle - SCMI entity handle
> + *
> + * Return: 0 on success, else appropriate SCMI error.
> + */
> +static int scmi_base_attributes_get(struct scmi_handle *handle)
> +{
> +       int ret;
> +       struct scmi_xfer *t;
> +       struct scmi_msg_resp_base_attributes *attr_info;
> +       struct scmi_revision_info *rev = handle->version;
> +
> +       ret = scmi_one_xfer_init(handle, PROTOCOL_ATTRIBUTES,
> +                                SCMI_PROTOCOL_BASE, 0, sizeof(*attr_info), &t);
> +       if (ret)
> +               return ret;
> +
> +       ret = scmi_do_xfer(handle, t);
> +       if (!ret) {
> +               attr_info = (struct scmi_msg_resp_base_attributes *)t->rx.buf;
> +               rev->num_protocols = attr_info->num_protocols;
> +               rev->num_agents = attr_info->num_agents;
> +       }
> +
> +       scmi_put_one_xfer(handle, t);
> +       return ret;
> +}
> +
> +/**
> + * scmi_base_vendor_id_get() - gets vendor/subvendor identifier ASCII string.
> + *
> + * @handle - SCMI entity handle
> + * @sub_vendor - specify true if sub-vendor ID is needed
> + *
> + * Return: 0 on success, else appropriate SCMI error.
> + */
> +static int scmi_base_vendor_id_get(struct scmi_handle *handle, bool sub_vendor)
> +{
> +       u8 cmd;
> +       int ret, size;
> +       char *vendor_id;
> +       struct scmi_xfer *t;
> +       struct scmi_revision_info *rev = handle->version;
> +
> +       if (sub_vendor) {
> +               cmd = BASE_DISCOVER_SUB_VENDOR;
> +               vendor_id = rev->sub_vendor_id;
> +               size = ARRAY_SIZE(rev->sub_vendor_id);
> +       } else {
> +               cmd = BASE_DISCOVER_VENDOR;
> +               vendor_id = rev->vendor_id;
> +               size = ARRAY_SIZE(rev->vendor_id);
> +       }
> +
> +       ret = scmi_one_xfer_init(handle, cmd, SCMI_PROTOCOL_BASE, 0, size, &t);
> +       if (ret)
> +               return ret;
> +
> +       ret = scmi_do_xfer(handle, t);
> +       if (!ret)
> +               memcpy(vendor_id, t->rx.buf, size);
> +
> +       scmi_put_one_xfer(handle, t);
> +       return ret;
> +}
> +
> +/**
> + * scmi_base_implementation_version_get() - gets a vendor-specific
> + *     implementation 32-bit version. The format of the version number is
> + *     vendor-specific
> + *
> + * @handle - SCMI entity handle
> + *
> + * Return: 0 on success, else appropriate SCMI error.
> + */
> +static int scmi_base_implementation_version_get(struct scmi_handle *handle)
> +{
> +       int ret;
> +       u32 *impl_ver;
> +       struct scmi_xfer *t;
> +       struct scmi_revision_info *rev = handle->version;
> +
> +       ret = scmi_one_xfer_init(handle, BASE_DISCOVER_IMPLEMENT_VERSION,
> +                                SCMI_PROTOCOL_BASE, 0, sizeof(*impl_ver), &t);
> +       if (ret)
> +               return ret;
> +
> +       ret = scmi_do_xfer(handle, t);
> +       if (ret) {
> +               impl_ver = (u32 *)t->rx.buf;
> +               rev->impl_ver = le32_to_cpu(*impl_ver);
> +       }
> +

Should be (!ret)

> +       scmi_put_one_xfer(handle, t);
> +       return ret;
> +}
> +
> +/**
> + * scmi_base_implementation_list_get() - gets the list of protocols it is
> + *     OSPM is allowed to access
> + *
> + * @handle - SCMI entity handle
> + * @protocols_imp - pointer to hold the list of protocol identifiers
> + *
> + * Return: 0 on success, else appropriate SCMI error.
> + */
> +static int scmi_base_implementation_list_get(struct scmi_handle *handle,
> +                                            u8 *protocols_imp)
> +{
> +       u8 *list;
> +       int ret, loop;
> +       struct scmi_xfer *t;
> +       __le32 *num_skip, *num_ret;
> +       u32 tot_num_ret = 0, loop_num_ret;
> +       struct device *dev = handle->dev;
> +
> +       ret = scmi_one_xfer_init(handle, BASE_DISCOVER_LIST_PROTOCOLS,
> +                                SCMI_PROTOCOL_BASE, sizeof(*num_skip), 0, &t);
> +       if (ret)
> +               return ret;
> +
> +       num_skip = (__le32 *)t->tx.buf;
> +       num_ret = (__le32 *)t->rx.buf;
> +       list = t->rx.buf + sizeof(*num_ret);
> +
> +       do {
> +               /* Set the number of protocols to be skipped/already read */
> +               *num_skip = cpu_to_le32(tot_num_ret);
> +
> +               ret = scmi_do_xfer(handle, t);
> +               if (ret)
> +                       break;
> +
> +               loop_num_ret = le32_to_cpu(*num_ret);
> +               if (tot_num_ret + loop_num_ret > MAX_PROTOCOLS_IMP) {
> +                       dev_err(dev, "No. of Protocol > MAX_PROTOCOLS_IMP");
> +                       break;
> +               }
> +
> +               for (loop = 0; loop < loop_num_ret; loop++)
> +                       protocols_imp[tot_num_ret + loop] = *(list + loop);
> +
> +               tot_num_ret += loop_num_ret;
> +       } while (loop_num_ret);
> +
> +       scmi_put_one_xfer(handle, t);
> +       return ret;
> +}
> +
> +/**
> + * scmi_base_discover_agent_get() - discover the name of an agent
> + *
> + * @handle - SCMI entity handle
> + * @id - Agent identifier
> + * @name - Agent identifier ASCII string
> + *
> + * An agent id of 0 is reserved to identify the platform itself.
> + * Generally operating system is represented as "OSPM"
> + *
> + * Return: 0 on success, else appropriate SCMI error.
> + */
> +static int
> +scmi_base_discover_agent_get(struct scmi_handle *handle, int id, char *name)
> +{
> +       int ret;
> +       struct scmi_xfer *t;
> +
> +       ret = scmi_one_xfer_init(handle, BASE_DISCOVER_AGENT,
> +                                SCMI_PROTOCOL_BASE, sizeof(__le32),
> +                                SCMI_MAX_STR_SIZE, &t);
> +       if (ret)
> +               return ret;
> +
> +       *(__le32 *)t->tx.buf = cpu_to_le32(id);
> +
> +       ret = scmi_do_xfer(handle, t);
> +       if (!ret)
> +               memcpy(name, t->rx.buf, SCMI_MAX_STR_SIZE);
> +
> +       scmi_put_one_xfer(handle, t);
> +       return ret;
> +}
> +
> +/**
> + * scmi_base_error_notifications_enable() - register/unregister for
> + *     notifications of errors in the platform
> + *
> + * @handle - SCMI entity handle
> + * @enable - Enable/Disable the notification
> + *
> + * Return: 0 on success, else appropriate SCMI error.
> + */
> +static int scmi_base_error_notifications_enable(struct scmi_handle *handle,
> +                                               bool enable)
> +{
> +       int ret;
> +       struct scmi_xfer *t;
> +
> +       ret = scmi_one_xfer_init(handle, BASE_NOTIFY_ERRORS, SCMI_PROTOCOL_BASE,
> +                                sizeof(__le32), 0, &t);
> +       if (ret)
> +               return ret;
> +
> +       *(__le32 *)t->tx.buf = cpu_to_le32(enable & BIT(0));
> +
> +       ret = scmi_do_xfer(handle, t);
> +
> +       scmi_put_one_xfer(handle, t);
> +       return ret;
> +}
> +
> +int scmi_base_protocol_init(struct scmi_handle *handle)
> +{
> +       int id, ret;
> +       u8 *prot_imp;
> +       u32 version;
> +       char name[SCMI_MAX_STR_SIZE];
> +       struct device *dev = handle->dev;
> +       struct scmi_revision_info *rev = handle->version;
> +
> +       ret = scmi_version_get(handle, SCMI_PROTOCOL_BASE, &version);
> +       if (ret)
> +               return ret;
> +
> +       prot_imp = devm_kcalloc(dev, MAX_PROTOCOLS_IMP, sizeof(u8), GFP_KERNEL);
> +       if (!prot_imp)
> +               return -ENOMEM;
> +
> +       rev->major_ver = PROTOCOL_REV_MAJOR(version),
> +       rev->minor_ver = PROTOCOL_REV_MINOR(version);
> +
> +       scmi_base_attributes_get(handle);
> +       scmi_base_vendor_id_get(handle, false);
> +       scmi_base_vendor_id_get(handle, true);
> +       scmi_base_implementation_version_get(handle);
> +       scmi_base_implementation_list_get(handle, prot_imp);
> +       scmi_base_error_notifications_enable(handle, true);
> +       scmi_setup_protocol_implemented(handle, prot_imp);
> +
> +       dev_info(dev, "SCMI Protocol %d.%d '%s:%s' Firmware Version 0x%x\n",
> +                rev->major_ver, rev->minor_ver, rev->vendor_id,
> +                rev->sub_vendor_id, rev->impl_ver);
> +       dev_dbg(dev, "Found %d protocol(s) %d agent(s)\n", rev->num_protocols,
> +               rev->num_agents);
> +
> +       for (id = 0; id < rev->num_agents; id++) {
> +               scmi_base_discover_agent_get(handle, id, name);
> +               dev_dbg(dev, "Agent %d: %s\n", id, name);
> +       }
> +
> +       return 0;
> +}
> diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h
> index a3038efa3a8d..24bc51dcc6c5 100644
> --- a/drivers/firmware/arm_scmi/common.h
> +++ b/drivers/firmware/arm_scmi/common.h
> @@ -19,9 +19,50 @@
>   */
>
>  #include <linux/completion.h>
> +#include <linux/device.h>
> +#include <linux/errno.h>
> +#include <linux/kernel.h>
>  #include <linux/scmi_protocol.h>
>  #include <linux/types.h>
>
> +#define PROTOCOL_REV_MINOR_BITS        16
> +#define PROTOCOL_REV_MINOR_MASK        ((1U << PROTOCOL_REV_MINOR_BITS) - 1)
> +#define PROTOCOL_REV_MAJOR(x)  ((x) >> PROTOCOL_REV_MINOR_BITS)
> +#define PROTOCOL_REV_MINOR(x)  ((x) & PROTOCOL_REV_MINOR_MASK)
> +#define MAX_PROTOCOLS_IMP      16
> +
> +enum scmi_std_protocol {
> +       SCMI_PROTOCOL_BASE = 0x10,
> +       SCMI_PROTOCOL_POWER = 0x11,
> +       SCMI_PROTOCOL_SYSTEM = 0x12,
> +       SCMI_PROTOCOL_PERF = 0x13,
> +       SCMI_PROTOCOL_CLOCK = 0x14,
> +       SCMI_PROTOCOL_SENSOR = 0x15,
> +};
> +
> +enum scmi_common_cmd {
> +       PROTOCOL_VERSION = 0x0,
> +       PROTOCOL_ATTRIBUTES = 0x1,
> +       PROTOCOL_MESSAGE_ATTRIBUTES = 0x2,
> +};
> +
> +/**
> + * struct scmi_msg_resp_prot_version - Response for a message
> + *
> + * @major_version: Major version of the ABI that firmware supports
> + * @minor_version: Minor version of the ABI that firmware supports
> + *
> + * In general, ABI version changes follow the rule that minor version increments
> + * are backward compatible. Major revision changes in ABI may not be
> + * backward compatible.
> + *
> + * Response to a generic message with message type SCMI_MSG_VERSION
> + */
> +struct scmi_msg_resp_prot_version {
> +       __le16 minor_version;
> +       __le16 major_version;
> +} __packed;
> +
>  /**
>   * struct scmi_msg_hdr - Message(Tx/Rx) header
>   *
> @@ -72,3 +113,8 @@ void scmi_put_one_xfer(struct scmi_handle *h, struct scmi_xfer *xfer);
>  int scmi_do_xfer(struct scmi_handle *h, struct scmi_xfer *xfer);
>  int scmi_one_xfer_init(struct scmi_handle *h, u8 msg_id, u8 msg_prot_id,
>                        size_t tx_size, size_t rx_size, struct scmi_xfer **p);
> +int scmi_version_get(struct scmi_handle *h, u8 protocol, u32 *version);
> +bool scmi_is_protocol_implemented(struct scmi_handle *h, u8 prot_id);
> +void scmi_setup_protocol_implemented(struct scmi_handle *handle, u8 *prot_imp);
> +
> +int scmi_base_protocol_init(struct scmi_handle *h);
> diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
> index f01e0643ac7d..7b653c932edc 100644
> --- a/drivers/firmware/arm_scmi/driver.c
> +++ b/drivers/firmware/arm_scmi/driver.c
> @@ -108,18 +108,22 @@ struct scmi_desc {
>   * @dev: Device pointer
>   * @desc: SoC description for this instance
>   * @handle: Instance of SCMI handle to send to clients
> + * @version: SCMI revision information containing protocol version,
> + *     implementation version and (sub-)vendor identification.
>   * @cl: Mailbox Client
>   * @tx_chan: Transmit mailbox channel
>   * @rx_chan: Receive mailbox channel
>   * @tx_payload: Transmit mailbox channel payload area
>   * @rx_payload: Receive mailbox channel payload area
>   * @minfo: Message info
> + * @protocols_imp: list of protocols implemented
>   * @node: list head
>   * @users: Number of users of this instance
>   */
>  struct scmi_info {
>         struct device *dev;
>         const struct scmi_desc *desc;
> +       struct scmi_revision_info version;
>         struct scmi_handle handle;
>         struct mbox_client cl;
>         struct mbox_chan *tx_chan;
> @@ -127,6 +131,7 @@ struct scmi_info {
>         void __iomem *tx_payload;
>         void __iomem *rx_payload;
>         struct scmi_xfers_info minfo;
> +       u8 *protocols_imp;
>         struct list_head node;
>         int users;
>  };
> @@ -445,6 +450,57 @@ int scmi_one_xfer_init(struct scmi_handle *handle, u8 msg_id, u8 msg_prot_id,
>  }
>
>  /**
> + * scmi_version_get() - command to get the revision of the SCMI entity
> + *
> + * @handle: Handle to SCMI entity information
> + *
> + * Updates the SCMI information in the internal data structure.
> + *
> + * Return: 0 if all went fine, else return appropriate error.
> + */
> +int scmi_version_get(struct scmi_handle *handle, u8 protocol, u32 *version)
> +{
> +       int ret;
> +       __le32 *rev_info;
> +       struct scmi_xfer *t;
> +
> +       ret = scmi_one_xfer_init(handle, PROTOCOL_VERSION, protocol, 0,
> +                                sizeof(*version), &t);
> +       if (ret)
> +               return ret;
> +
> +       rev_info = (__le32 *)t->rx.buf;
> +
> +       ret = scmi_do_xfer(handle, t);
> +       if (!ret)
> +               *version = le32_to_cpu(*rev_info);
> +
> +       scmi_put_one_xfer(handle, t);
> +       return ret;
> +}
> +
> +void scmi_setup_protocol_implemented(struct scmi_handle *handle, u8 *prot_imp)
> +{
> +       struct scmi_info *info = handle_to_scmi_info(handle);
> +
> +       info->protocols_imp = prot_imp;
> +}
> +
> +bool scmi_is_protocol_implemented(struct scmi_handle *handle, u8 prot_id)
> +{
> +       int i;
> +       struct scmi_info *info = handle_to_scmi_info(handle);
> +
> +       if (!info->protocols_imp)
> +               return false;
> +
> +       for (i = 0; i < MAX_PROTOCOLS_IMP; i++)
> +               if (info->protocols_imp[i] == prot_id)
> +                       return true;
> +       return false;
> +}
> +
> +/**
>   * scmi_handle_get() - Get the  SCMI handle for a device
>   *
>   * @dev: pointer to device for which we want SCMI handle
> @@ -642,6 +698,11 @@ static int scmi_probe(struct platform_device *pdev)
>
>         desc = of_match_device(scmi_of_match, dev)->data;
>
> +       if (of_property_match_string(np, "method", "mailbox-doorbell") < 0) {
> +               dev_err(dev, "invalid method property in %s\n", np->full_name);
> +               return -EINVAL;
> +       }
> +
>         info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
>         if (!info)
>                 return -ENOMEM;
> @@ -687,6 +748,12 @@ static int scmi_probe(struct platform_device *pdev)
>
>         handle = &info->handle;
>         handle->dev = info->dev;
> +       handle->version = &info->version;
> +       ret = scmi_base_protocol_init(handle);
> +       if (ret) {
> +               dev_err(dev, "unable to communicate with SCMI(%d)\n", ret);
> +               goto out;
> +       }
>
>         mutex_lock(&scmi_list_mutex);
>         list_add_tail(&info->node, &scmi_list);
> diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h
> index 0c795a765110..901976fe211f 100644
> --- a/include/linux/scmi_protocol.h
> +++ b/include/linux/scmi_protocol.h
> @@ -17,13 +17,41 @@
>   */
>  #include <linux/types.h>
>
> +#define SCMI_MAX_STR_SIZE              16
> +
> +/**
> + * struct scmi_revision_info - version information structure
> + *
> + * @major_ver: Major ABI version. Change here implies risk of backward
> + *     compatibility break.
> + * @minor_ver: Minor ABI version. Change here implies new feature addition,
> + *     or compatible change in ABI.
> + * @num_protocols: Number of protocols that are implemented, excluding the
> + *     base protocol.
> + * @num_agents: Number of agents in the system.
> + * @impl_ver: A vendor-specific implementation version.
> + * @vendor_id: A vendor identifier(Null terminated ASCII string)
> + * @sub_vendor_id: A sub-vendor identifier(Null terminated ASCII string)
> + */
> +struct scmi_revision_info {
> +       u16 major_ver;
> +       u16 minor_ver;
> +       u8 num_protocols;
> +       u8 num_agents;
> +       u32 impl_ver;
> +       char vendor_id[SCMI_MAX_STR_SIZE];
> +       char sub_vendor_id[SCMI_MAX_STR_SIZE];
> +};
> +
>  /**
>   * struct scmi_handle - Handle returned to ARM SCMI clients for usage.
>   *
>   * @dev: pointer to the SCMI device
> + * @version: pointer to the structure containing SCMI version information
>   */
>  struct scmi_handle {
>         struct device *dev;
> +       struct scmi_revision_info *version;
>  };
>
>  #if IS_REACHABLE(CONFIG_ARM_SCMI_PROTOCOL)
> --
> 2.7.4
>

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

* Re: [RFC PATCH 5/8] firmware: arm_scmi: add initial support for clock protocol
  2017-06-07 16:10 ` [RFC PATCH 5/8] firmware: arm_scmi: add initial support for clock protocol Sudeep Holla
@ 2017-06-07 19:19   ` Roy Franz
  0 siblings, 0 replies; 23+ messages in thread
From: Roy Franz @ 2017-06-07 19:19 UTC (permalink / raw)
  To: Sudeep Holla
  Cc: linux-kernel, devicetree, Roy Franz, Harb Abdulhamid,
	Nishanth Menon, Arnd Bergmann

On Wed, Jun 7, 2017 at 9:10 AM, Sudeep Holla <sudeep.holla@arm.com> wrote:
> The clock protocol is intended for management of clocks. It is used to
> enable or disable clocks, and to set and get the clock rates. This
> protocol provides commands to describe the protocol version, discover
> various implementation specific attributes, describe a clock, enable
> and disable a clock and get/set the rate of the clock synchronously or
> asynchronously.
>
> This patch adds initial support for the clock protocol.
>
> Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
> ---
>  drivers/firmware/arm_scmi/Makefile |   2 +-
>  drivers/firmware/arm_scmi/clock.c  | 340 +++++++++++++++++++++++++++++++++++++
>  drivers/firmware/arm_scmi/common.h |   1 +
>  include/linux/scmi_protocol.h      |  18 ++
>  4 files changed, 360 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/firmware/arm_scmi/clock.c
>
> diff --git a/drivers/firmware/arm_scmi/Makefile b/drivers/firmware/arm_scmi/Makefile
> index 159de726ee45..6836b1f38f7f 100644
> --- a/drivers/firmware/arm_scmi/Makefile
> +++ b/drivers/firmware/arm_scmi/Makefile
> @@ -1,2 +1,2 @@
>  obj-$(CONFIG_ARM_SCMI_PROTOCOL)        = arm_scmi.o
> -arm_scmi-y = base.o driver.o perf.o
> +arm_scmi-y = base.o clock.o driver.o perf.o
> diff --git a/drivers/firmware/arm_scmi/clock.c b/drivers/firmware/arm_scmi/clock.c
> new file mode 100644
> index 000000000000..e02827a48ab7
> --- /dev/null
> +++ b/drivers/firmware/arm_scmi/clock.c
> @@ -0,0 +1,340 @@
> +/*
> + * System Control and Management Interface (SCMI) Clock Protocol
> + *
> + * Copyright (C) 2017 ARM Ltd.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
> + * more details.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include "common.h"
> +
> +enum scmi_clock_protocol_cmd {
> +       CLOCK_ATTRIBUTES = 0x3,
> +       CLOCK_DESCRIBE_RATES = 0x4,
> +       CLOCK_RATE_SET = 0x5,
> +       CLOCK_RATE_GET = 0x6,
> +       CLOCK_CONFIG_SET = 0x7,
> +};
> +
> +struct scmi_msg_resp_clock_protocol_attributes {
> +       __le16 num_clocks;
> +           u8 max_async_req;
> +           u8 reserved;
> +} __packed;
> +
> +struct scmi_msg_resp_clock_attributes {
> +       __le32 attributes;
> +#define        CLOCK_ENABLE    BIT(0)
> +           u8 name[SCMI_MAX_STR_SIZE];
> +} __packed;
> +
> +struct scmi_clock_set_config {
> +       __le32 id;
> +       __le32 attributes;
> +} __packed;
> +
> +struct scmi_msg_clock_describe_rates {
> +       __le32 id;
> +       __le32 rate_index;
> +} __packed;
> +
> +struct scmi_msg_resp_clock_describe_rates {
> +       __le16 num_returned;
> +#define NUM_RETURNED_MASK      (0xfff)
> +#define RATE_DISCRETE(x)       ((x) & BIT(12))
> +       __le16 num_remaining;
> +       struct {
> +               __le32 value_low;
> +               __le32 value_high;
> +       } rate[0];
> +#define RATE_TO_U64(X)         \
> +({                             \
> +       typeof(X) x = (X);      \
> +       le32_to_cpu((x).value_low) | (u64)le32_to_cpu((x).value_high) >> 32; \
> +})
> +} __packed;
> +
> +struct scmi_clock_set_rate {
> +       __le32 flags;
> +#define CLOCK_SET_ASYSC                BIT(0)
> +#define CLOCK_SET_DELAYED      BIT(1)
> +#define CLOCK_ROUND_UP         BIT(2)
> +#define CLOCK_ROUND_AUTO       BIT(3)
> +       __le32 id;
> +       __le32 value_low;
> +       __le32 value_high;
> +} __packed;
> +
> +struct clock_info {
> +       u32 attributes;
> +       char name[SCMI_MAX_STR_SIZE];
> +       union {
> +               int num_rates;
> +               u64 rates[MAX_NUM_RATES];
> +               struct {
> +                       u64 min_rate;
> +                       u64 max_rate;
> +                       u64 step_size;
> +               } range;
> +       };
> +};
> +
> +struct scmi_clock_info {
> +       int num_clocks;
> +       int max_async_req;
> +       struct clock_info *clk;
> +};
> +
> +static struct scmi_clock_info clocks;
> +
> +static int scmi_clock_protocol_attributes_get(struct scmi_handle *handle,
> +                                             struct scmi_clock_info *clocks)
> +{
> +       int ret;
> +       struct scmi_xfer *t;
> +       struct scmi_msg_resp_clock_protocol_attributes *attr;
> +
> +       ret = scmi_one_xfer_init(handle, PROTOCOL_ATTRIBUTES,
> +                                SCMI_PROTOCOL_CLOCK, 0, sizeof(*attr), &t);
> +       if (ret)
> +               return ret;
> +
> +       attr = (struct scmi_msg_resp_clock_protocol_attributes *)t->rx.buf;
> +
> +       ret = scmi_do_xfer(handle, t);
> +       if (!ret) {
> +               clocks->num_clocks = le16_to_cpu(attr->num_clocks);
> +               clocks->max_async_req = attr->max_async_req;
> +       }
> +
> +       scmi_put_one_xfer(handle, t);
> +       return ret;
> +}
> +
> +static int scmi_clock_attributes_get(struct scmi_handle *handle, u32 clk_id,
> +                                    struct clock_info *clk)
> +{
> +       int ret;
> +       struct scmi_xfer *t;
> +       struct scmi_msg_resp_clock_attributes *attr;
> +
> +       ret = scmi_one_xfer_init(handle, CLOCK_ATTRIBUTES, SCMI_PROTOCOL_CLOCK,
> +                                sizeof(clk_id), sizeof(*attr), &t);
> +       if (ret)
> +               return ret;
> +
> +       *(__le32 *)t->tx.buf = cpu_to_le32(clk_id);
> +       attr = (struct scmi_msg_resp_clock_attributes *)t->rx.buf;
> +
> +       ret = scmi_do_xfer(handle, t);
> +       if (!ret) {
> +               clk->attributes = le32_to_cpu(attr->attributes);
> +               memcpy(clk->name, attr->name, SCMI_MAX_STR_SIZE);
> +       }
> +
> +       scmi_put_one_xfer(handle, t);
> +       return ret;
> +}
> +
> +static int scmi_clock_describe_rates_get(struct scmi_handle *handle, u32 clk_id,
> +                                        struct clock_info *clk)
> +{
> +       u64 *rate;
> +       int ret, cnt;
> +       bool rate_discrete;
> +       u32 tot_rate_cnt = 0;
> +       u16 num_returned, num_remaining;
> +       struct scmi_xfer *t;
> +       struct scmi_msg_clock_describe_rates *clk_desc;
> +       struct scmi_msg_resp_clock_describe_rates *rlist;
> +
> +       ret = scmi_one_xfer_init(handle, CLOCK_DESCRIBE_RATES,
> +                                SCMI_PROTOCOL_CLOCK, sizeof(*clk_desc), 0, &t);
> +       if (ret)
> +               return ret;
> +
> +       clk_desc = (struct scmi_msg_clock_describe_rates *)t->tx.buf;
> +       rlist = (struct scmi_msg_resp_clock_describe_rates *)t->rx.buf;
> +
> +       do {
> +               clk_desc->id = cpu_to_le32(clk_id);
> +               /* Set the number of rates to be skipped/already read */
> +               clk_desc->rate_index = cpu_to_le32(tot_rate_cnt);
> +
> +               ret = scmi_do_xfer(handle, t);
> +               if (ret)
> +                       break;
> +
> +               num_returned = le16_to_cpu(rlist->num_returned);
> +               num_remaining = le16_to_cpu(rlist->num_remaining);
> +               rate_discrete = RATE_DISCRETE(num_remaining);
> +               num_remaining &= NUM_RETURNED_MASK;
> +
> +               if (tot_rate_cnt + num_returned > MAX_NUM_RATES) {
> +                       dev_err(handle->dev, "No. of rates > MAX_NUM_RATES");
> +                       break;
> +               }
> +
> +               if (!rate_discrete) {
> +                       clk->range.min_rate = RATE_TO_U64(rlist->rate[0]);
> +                       clk->range.max_rate = RATE_TO_U64(rlist->rate[1]);
> +                       clk->range.step_size = RATE_TO_U64(rlist->rate[2]);
> +                       dev_dbg(handle->dev, "Min %llu Max %llu Step %llu Hz\n",
> +                               clk->range.min_rate, clk->range.max_rate,
> +                               clk->range.step_size);
> +                       break;
> +               }
> +
> +               rate = &clk->rates[tot_rate_cnt];
> +               for (cnt = 0; cnt < num_returned; cnt++, rate++) {
> +                       *rate = RATE_TO_U64(rlist->rate[cnt]);
> +                       dev_dbg(handle->dev, "Rate %llu Hz\n", *rate);
> +               }
> +
> +               tot_rate_cnt += num_returned;
> +               /*
> +                * check for both returned and remaining to avoid infinite
> +                * loop due to buggy firmware
> +                */
> +       } while (num_returned && num_remaining);
> +
> +       clk->num_rates = tot_rate_cnt;
> +
> +       scmi_put_one_xfer(handle, t);
> +       return ret;
> +}
> +
> +static int
> +scmi_clock_rate_get(struct scmi_handle *handle, u32 clk_id, u64 *value)
> +{
> +       int ret;
> +       struct scmi_xfer *t;
> +
> +       ret = scmi_one_xfer_init(handle, CLOCK_RATE_GET, SCMI_PROTOCOL_CLOCK,
> +                                sizeof(__le32), sizeof(u64), &t);
> +       if (ret)
> +               return ret;
> +
> +       *(__le32 *)t->tx.buf = cpu_to_le32(clk_id);
> +
> +       ret = scmi_do_xfer(handle, t);
> +       if (!ret) {
> +               __le32 *pval = (__le32 *)t->rx.buf;
> +
> +               *value = le32_to_cpu(*pval);
> +               *value  |= le32_to_cpu(*(pval + 1));
missing shift for upper bits
> +       }
> +
> +       scmi_put_one_xfer(handle, t);
> +       return ret;
> +}
> +
> +static int scmi_clock_rate_set(struct scmi_handle *handle, u32 clk_id,
> +                              u32 config, u64 rate)
> +{
> +       int ret;
> +       struct scmi_xfer *t;
> +       struct scmi_clock_set_rate *cfg;
> +
> +       ret = scmi_one_xfer_init(handle, CLOCK_RATE_SET, SCMI_PROTOCOL_CLOCK,
> +                                sizeof(*cfg), 0, &t);
> +       if (ret)
> +               return ret;
> +
> +       cfg = (struct scmi_clock_set_rate *)t->tx.buf;
> +       cfg->flags = cpu_to_le32(config);
> +       cfg->id = cpu_to_le32(clk_id);
> +       cfg->value_low = cpu_to_le32(rate & 0xffffffff);
> +       cfg->value_high = cpu_to_le32(rate >> 32);
> +
> +       ret = scmi_do_xfer(handle, t);
> +
> +       scmi_put_one_xfer(handle, t);
> +       return ret;
> +}
> +
> +static int
> +scmi_clock_config_set(struct scmi_handle *handle, u32 clk_id, u32 config)
> +{
> +       int ret;
> +       struct scmi_xfer *t;
> +       struct scmi_clock_set_config *cfg;
> +
> +       ret = scmi_one_xfer_init(handle, CLOCK_CONFIG_SET, SCMI_PROTOCOL_CLOCK,
> +                                sizeof(*cfg), 0, &t);
> +       if (ret)
> +               return ret;
> +
> +       cfg = (struct scmi_clock_set_config *)t->tx.buf;
> +       cfg->id = cpu_to_le32(clk_id);
> +       cfg->attributes = cpu_to_le32(config);
> +
> +       ret = scmi_do_xfer(handle, t);
> +
> +       scmi_put_one_xfer(handle, t);
> +       return ret;
> +}
> +
> +static int scmi_clock_enable(struct scmi_handle *handle, u32 clk_id)
> +{
> +       return scmi_clock_config_set(handle, clk_id, CLOCK_ENABLE);
> +}
> +
> +static int scmi_clock_disable(struct scmi_handle *handle, u32 clk_id)
> +{
> +       return scmi_clock_config_set(handle, clk_id, 0);
> +}
> +
> +static struct scmi_clk_ops clk_ops = {
> +       .rate_get = scmi_clock_rate_get,
> +       .rate_set = scmi_clock_rate_set,
> +       .enable = scmi_clock_enable,
> +       .disable = scmi_clock_disable,
> +};
> +
> +int scmi_clock_protocol_init(struct scmi_handle *handle)
> +{
> +       int clk_id;
> +       u32 version;
> +
> +       if (!scmi_is_protocol_implemented(handle, SCMI_PROTOCOL_CLOCK)) {
> +               dev_err(handle->dev, "SCMI Clock protocol not implemented\n");
> +               return -EPROTONOSUPPORT;
> +       }
> +
> +       scmi_version_get(handle, SCMI_PROTOCOL_CLOCK, &version);
> +
> +       dev_dbg(handle->dev, "Clock Version %d.%d\n",
> +               PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
> +
> +       scmi_clock_protocol_attributes_get(handle, &clocks);
> +
> +       clocks.clk = devm_kcalloc(handle->dev, clocks.num_clocks,
> +                                 sizeof(struct clock_info), GFP_KERNEL);
> +       if (!clocks.clk)
> +               return -ENOMEM;
> +
> +       dev_info(handle->dev, "Num Clock %d Max Async Req %d\n",
> +                clocks.num_clocks, clocks.max_async_req);
> +
> +       for (clk_id = 0; clk_id < clocks.num_clocks; clk_id++) {
> +               struct clock_info *clk = clocks.clk + clk_id;
> +
> +               scmi_clock_attributes_get(handle, clk_id, clk);
> +               scmi_clock_describe_rates_get(handle, clk_id, clk);
> +       }
> +
> +       handle->clk_ops = &clk_ops;
> +
> +       return 0;
> +}
> diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h
> index e153f05720e4..32873a315c76 100644
> --- a/drivers/firmware/arm_scmi/common.h
> +++ b/drivers/firmware/arm_scmi/common.h
> @@ -31,6 +31,7 @@
>  #define PROTOCOL_REV_MINOR(x)  ((x) & PROTOCOL_REV_MINOR_MASK)
>  #define MAX_PROTOCOLS_IMP      16
>  #define MAX_OPPS               16
> +#define MAX_NUM_RATES          16
>
>  enum scmi_std_protocol {
>         SCMI_PROTOCOL_BASE = 0x10,
> diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h
> index 0f25a3defb4c..75ba10441f54 100644
> --- a/include/linux/scmi_protocol.h
> +++ b/include/linux/scmi_protocol.h
> @@ -46,6 +46,22 @@ struct scmi_revision_info {
>  struct scmi_handle;
>
>  /**
> + * struct scmi_clk_ops - represents the various operations provided
> + *     by SCMI Clock Protocol
> + *
> + * @rate_get: request the current clock rate of a clock
> + * @rate_set: set the clock rate of a clock
> + * @enable: enables the specified clock
> + * @disable: disables the specified clock
> + */
> +struct scmi_clk_ops {
> +       int (*rate_get)(struct scmi_handle *, u32, u64*);
> +       int (*rate_set)(struct scmi_handle *, u32, u32, u64);
> +       int (*enable)(struct scmi_handle *, u32);
> +       int (*disable)(struct scmi_handle *, u32);
> +};
> +
> +/**
>   * struct scmi_perf_ops - represents the various operations provided
>   *     by SCMI Performance Protocol
>   *
> @@ -73,11 +89,13 @@ struct scmi_perf_ops {
>   * @dev: pointer to the SCMI device
>   * @version: pointer to the structure containing SCMI version information
>   * @perf_ops: pointer to set of performance protocol operations
> + * @clk_ops: pointer to set of clock protocol operations
>   */
>  struct scmi_handle {
>         struct device *dev;
>         struct scmi_revision_info *version;
>         struct scmi_perf_ops *perf_ops;
> +       struct scmi_clk_ops *clk_ops;
>  };
>
>  struct scmi_opp {
> --
> 2.7.4
>

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

* Re: [RFC PATCH 7/8] firmware: arm_scmi: add initial support for sensor protocol
  2017-06-07 16:10 ` [RFC PATCH 7/8] firmware: arm_scmi: add initial support for sensor protocol Sudeep Holla
@ 2017-06-07 19:19   ` Roy Franz
  0 siblings, 0 replies; 23+ messages in thread
From: Roy Franz @ 2017-06-07 19:19 UTC (permalink / raw)
  To: Sudeep Holla
  Cc: linux-kernel, devicetree, Roy Franz, Harb Abdulhamid,
	Nishanth Menon, Arnd Bergmann

On Wed, Jun 7, 2017 at 9:10 AM, Sudeep Holla <sudeep.holla@arm.com> wrote:
> The sensor protocol provides functions to manage platform sensors, and
> provides the commands to describe the protocol version and the various
> attribute flags. It also provides commands to discover various sensors
> implemented and managed by the platform, read any sensor synchronously
> or asynchronously as allowed by the platform, program sensor attributes
> and/or configurations, if applicable.
>
> This patch adds support for most of the above features.
>
> Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
> ---
>  drivers/firmware/arm_scmi/Makefile  |   2 +-
>  drivers/firmware/arm_scmi/sensors.c | 269 ++++++++++++++++++++++++++++++++++++
>  include/linux/scmi_protocol.h       |  17 +++
>  3 files changed, 287 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/firmware/arm_scmi/sensors.c
>
> diff --git a/drivers/firmware/arm_scmi/Makefile b/drivers/firmware/arm_scmi/Makefile
> index 52ecc08556a2..f9dee5ad0aa0 100644
> --- a/drivers/firmware/arm_scmi/Makefile
> +++ b/drivers/firmware/arm_scmi/Makefile
> @@ -1,2 +1,2 @@
>  obj-$(CONFIG_ARM_SCMI_PROTOCOL)        = arm_scmi.o
> -arm_scmi-y = base.o clock.o driver.o perf.o power.o
> +arm_scmi-y = base.o clock.o driver.o perf.o power.o sensors.o
> diff --git a/drivers/firmware/arm_scmi/sensors.c b/drivers/firmware/arm_scmi/sensors.c
> new file mode 100644
> index 000000000000..d8cee8c6cb99
> --- /dev/null
> +++ b/drivers/firmware/arm_scmi/sensors.c
> @@ -0,0 +1,269 @@
> +/*
> + * System Control and Management Interface (SCMI) Sensor Protocol
> + *
> + * Copyright (C) 2017 ARM Ltd.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
> + * more details.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include "common.h"
> +
> +enum scmi_sensor_protocol_cmd {
> +       SENSOR_DESCRIPTION_GET = 0x3,
> +       SENSOR_CONFIG_SET = 0x4,
> +       SENSOR_TRIP_POINT_SET = 0x5,
> +       SENSOR_READING_GET = 0x6,
> +};
> +
> +struct scmi_msg_resp_sensor_attributes {
> +       __le16 num_sensors;
> +           u8 max_requests;
> +           u8 reserved;
> +       __le32 reg_addr_low;
> +       __le32 reg_addr_high;
> +       __le32 reg_size;
> +} __packed;
> +
> +struct scmi_msg_resp_sensor_description {
> +       __le16 num_returned;
> +       __le16 num_remaining;
> +       struct {
> +               __le32 id;
> +               __le32 attributes_low;
> +#define SUPPORTS_ASYNC_READ(x) ((x) & BIT(31))
> +#define NUM_TRIP_POINTS(x)     (((x) >> 4) & 0xff)
> +               __le32 attributes_high;
> +#define SENSOR_TYPE(x)         ((x) & 0xff)
> +#define SENSOR_SCALE(x)                (((x) >> 11) & 0x3f)
> +#define SENSOR_UPDATE_SCALE(x) (((x) >> 22) & 0x1f)
> +#define SENSOR_UPDATE_BASE(x)  (((x) >> 27) & 0x1f)
> +                   u8 name[SCMI_MAX_STR_SIZE];
> +       } desc[0];
> +} __packed;
> +
> +struct scmi_msg_set_sensor_config {
> +       __le32 id;
> +       __le32 event_control;
> +} __packed;
> +
> +struct scmi_msg_set_sensor_trip_point {
> +       __le32 id;
> +       __le32 event_control;
> +#define SENSOR_TP_EVENT_MASK   (0x3)
> +#define SENSOR_TP_DISABLED     0x0
> +#define SENSOR_TP_POSITIVE     0x1
> +#define SENSOR_TP_NEGATIVE     0x2
> +#define SENSOR_TP_BOTH         0x3
> +#define SENSOR_TP_ID(x)                (((x) & 0xff) << 4)
> +       __le32 value_low;
> +       __le32 value_high;
> +} __packed;
> +
> +struct scmi_msg_sensor_reading_get {
> +       __le32 id;
> +       __le32 flags;
> +#define SENSOR_READ_ASYNC      BIT(0)
> +} __packed;
> +
> +struct scmi_sensors_info {
> +       int num_sensors;
> +       int max_requests;
> +       u64 reg_addr;
> +       u32 reg_size;
> +};
> +
> +static struct scmi_sensors_info sensor_info;
> +
> +static int scmi_sensor_attributes_get(struct scmi_handle *handle,
> +                                     struct scmi_sensors_info *sensor_info)
> +{
> +       int ret;
> +       struct scmi_xfer *t;
> +       struct scmi_msg_resp_sensor_attributes *attr;
> +
> +       ret = scmi_one_xfer_init(handle, PROTOCOL_ATTRIBUTES,
> +                                SCMI_PROTOCOL_SENSOR, 0, sizeof(*attr), &t);
> +       if (ret)
> +               return ret;
> +
> +       attr = (struct scmi_msg_resp_sensor_attributes *)t->rx.buf;
> +
> +       ret = scmi_do_xfer(handle, t);
> +       if (!ret) {
> +               sensor_info->num_sensors = le16_to_cpu(attr->num_sensors);
> +               sensor_info->max_requests = le16_to_cpu(attr->max_requests);
> +               sensor_info->reg_addr = le32_to_cpu(attr->reg_addr_low) |
> +                               (u64)le32_to_cpu(attr->reg_addr_high) << 32;
> +               sensor_info->reg_size = le32_to_cpu(attr->reg_size);
> +       }
> +
> +       scmi_put_one_xfer(handle, t);
> +       return ret;
> +}
> +
> +static int scmi_sensor_description_get(struct scmi_handle *handle)
> +{
> +       int ret, cnt;
> +       u32 desc_index = 0;
> +       u16 num_returned, num_remaining;
> +       struct scmi_xfer *t;
> +       struct scmi_msg_resp_sensor_description *sensors;
> +
> +       ret = scmi_one_xfer_init(handle, SENSOR_DESCRIPTION_GET,
> +                                SCMI_PROTOCOL_SENSOR, sizeof(__le32), 0, &t);
> +       if (ret)
> +               return ret;
> +
> +       sensors = (struct scmi_msg_resp_sensor_description *)t->rx.buf;
> +
> +       do {
> +               /* Set the number of sensors to be skipped/already read */
> +               *(__le32 *)t->tx.buf = cpu_to_le32(desc_index);
> +
> +               ret = scmi_do_xfer(handle, t);
> +               if (ret)
> +                       break;
> +
> +               num_returned = le16_to_cpu(sensors->num_returned);
> +               num_remaining = le16_to_cpu(sensors->num_remaining);
> +
> +               if (desc_index + num_returned > sensor_info.num_sensors) {
> +                       dev_err(handle->dev, "No. of sensors can't exceed %d",
> +                               sensor_info.num_sensors);
> +                       break;
> +               }
> +
> +               for (cnt = 0; cnt < num_returned; cnt++) {
> +                       dev_dbg(handle->dev, "Id %d, AttrH 0x%x AttrL 0x%x %s\n",
> +                               le32_to_cpu(sensors->desc[cnt].id),
> +                               le32_to_cpu(sensors->desc[cnt].attributes_high),
> +                               le32_to_cpu(sensors->desc[cnt].attributes_low),
> +                               sensors->desc[cnt].name);
> +               }
> +
> +               desc_index += num_returned;
> +               /*
> +                * check for both returned and remaining to avoid infinite
> +                * loop due to buggy firmware
> +                */
> +       } while (num_returned && num_remaining);
> +
> +       scmi_put_one_xfer(handle, t);
> +       return ret;
> +}
> +
> +static int
> +scmi_sensor_configuration_set(struct scmi_handle *handle, u32 sensor_id)
> +{
> +       int ret;
> +       u32 evt_cntl = BIT(0);
> +       struct scmi_xfer *t;
> +       struct scmi_msg_set_sensor_config *cfg;
> +
> +       ret = scmi_one_xfer_init(handle, SENSOR_CONFIG_SET,
> +                                SCMI_PROTOCOL_SENSOR, sizeof(*cfg), 0, &t);
> +       if (ret)
> +               return ret;
> +
> +       cfg = (struct scmi_msg_set_sensor_config *)t->tx.buf;
> +       cfg->id = cpu_to_le32(sensor_id);
> +       cfg->event_control = cpu_to_le32(evt_cntl);
> +
> +       ret = scmi_do_xfer(handle, t);
> +
> +       scmi_put_one_xfer(handle, t);
> +       return ret;
> +}
> +
> +static int scmi_sensor_trip_point_set(struct scmi_handle *handle, u32 sensor_id,
> +                                     u8 trip_id, u64 trip_value)
> +{
> +       int ret;
> +       u32 evt_cntl = SENSOR_TP_BOTH;
> +       struct scmi_xfer *t;
> +       struct scmi_msg_set_sensor_trip_point *trip;
> +
> +       ret = scmi_one_xfer_init(handle, SENSOR_TRIP_POINT_SET,
> +                                SCMI_PROTOCOL_SENSOR, sizeof(*trip), 0, &t);
> +       if (ret)
> +               return ret;
> +
> +       trip = (struct scmi_msg_set_sensor_trip_point *)t->tx.buf;
> +       trip->id = cpu_to_le32(sensor_id);
> +       trip->event_control = cpu_to_le32(evt_cntl | SENSOR_TP_ID(trip_id));
> +       trip->value_low = cpu_to_le32(trip_value & 0xffffffff);
> +       trip->value_high = cpu_to_le32(trip_value >> 32);
> +
> +       ret = scmi_do_xfer(handle, t);
> +
> +       scmi_put_one_xfer(handle, t);
> +       return ret;
> +}
> +
> +static int scmi_sensor_reading_get(struct scmi_handle *handle, u32 sensor_id,
> +                                  bool async, u64 *value)
> +{
> +       int ret;
> +       struct scmi_xfer *t;
> +       struct scmi_msg_sensor_reading_get *sensor;
> +
> +       ret = scmi_one_xfer_init(handle, SENSOR_READING_GET,
> +                                SCMI_PROTOCOL_SENSOR, sizeof(*sensor),
> +                                sizeof(u64), &t);
> +       if (ret)
> +               return ret;
> +
> +       sensor = (struct scmi_msg_sensor_reading_get *)t->tx.buf;
> +       sensor->id = cpu_to_le32(sensor_id);
> +       sensor->flags = cpu_to_le32(async ? SENSOR_READ_ASYNC : 0);
> +
> +       ret = scmi_do_xfer(handle, t);
> +       if (!ret) {
> +               __le32 *pval = (__le32 *)t->rx.buf;
> +
> +               *value = le32_to_cpu(*pval);
> +               *value  |= le32_to_cpu(*(pval + 1));
missing shift for upper bits

> +       }
> +
> +       scmi_put_one_xfer(handle, t);
> +       return ret;
> +}
> +
> +static struct scmi_sensor_ops sensor_ops = {
> +       .configuration_set = scmi_sensor_configuration_set,
> +       .trip_point_set = scmi_sensor_trip_point_set,
> +       .reading_get = scmi_sensor_reading_get,
> +};
> +
> +int scmi_sensors_protocol_init(struct scmi_handle *handle)
> +{
> +       u32 version;
> +
> +       if (!scmi_is_protocol_implemented(handle, SCMI_PROTOCOL_SENSOR)) {
> +               dev_err(handle->dev, "SCMI Sensor protocol not implemented\n");
> +               return -EPROTONOSUPPORT;
> +       }
> +
> +       scmi_version_get(handle, SCMI_PROTOCOL_SENSOR, &version);
> +
> +       dev_dbg(handle->dev, "Sensor Version %d.%d\n",
> +               PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
> +
> +       scmi_sensor_attributes_get(handle, &sensor_info);
> +
> +       scmi_sensor_description_get(handle);
> +
> +       handle->sensor_ops = &sensor_ops;
> +
> +       return 0;
> +}
> diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h
> index 50a5816a295d..2b7836b4cb5a 100644
> --- a/include/linux/scmi_protocol.h
> +++ b/include/linux/scmi_protocol.h
> @@ -99,6 +99,21 @@ struct scmi_power_ops {
>  };
>
>  /**
> + * struct scmi_sensor_ops - represents the various operations provided
> + *     by SCMI Sensor Protocol
> + *
> + * @configuration_set: control notifications on cross-over events for
> + *     the trip-points
> + * @trip_point_set: selects and configures a trip-point of interest
> + * @reading_get: gets the current value of the sensor
> + */
> +struct scmi_sensor_ops {
> +       int (*configuration_set)(struct scmi_handle *, u32);
> +       int (*trip_point_set)(struct scmi_handle *, u32, u8, u64);
> +       int (*reading_get)(struct scmi_handle *, u32, bool, u64 *);
> +};
> +
> +/**
>   * struct scmi_handle - Handle returned to ARM SCMI clients for usage.
>   *
>   * @dev: pointer to the SCMI device
> @@ -106,6 +121,7 @@ struct scmi_power_ops {
>   * @power_ops: pointer to set of power protocol operations
>   * @perf_ops: pointer to set of performance protocol operations
>   * @clk_ops: pointer to set of clock protocol operations
> + * @sensor_ops: pointer to set of sensor protocol operations
>   */
>  struct scmi_handle {
>         struct device *dev;
> @@ -113,6 +129,7 @@ struct scmi_handle {
>         struct scmi_power_ops *power_ops;
>         struct scmi_perf_ops *perf_ops;
>         struct scmi_clk_ops *clk_ops;
> +       struct scmi_sensor_ops *sensor_ops;
>  };
>
>  struct scmi_opp {
> --
> 2.7.4
>

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

* Re: [RFC PATCH 6/8] firmware: arm_scmi: add initial support for power protocol
  2017-06-07 16:10 ` [RFC PATCH 6/8] firmware: arm_scmi: add initial support for power protocol Sudeep Holla
@ 2017-06-07 20:38   ` Arnd Bergmann
  2017-06-08  9:39     ` Sudeep Holla
  0 siblings, 1 reply; 23+ messages in thread
From: Arnd Bergmann @ 2017-06-07 20:38 UTC (permalink / raw)
  To: Sudeep Holla
  Cc: Linux Kernel Mailing List, devicetree, Roy Franz,
	Harb Abdulhamid, Nishanth Menon

On Wed, Jun 7, 2017 at 6:10 PM, Sudeep Holla <sudeep.holla@arm.com> wrote:

> +struct scmi_msg_resp_power_attributes {
> +       __le16 num_domains;
> +       __le16 reserved;
> +       __le32 stats_addr_low;
> +       __le32 stats_addr_high;
> +       __le32 stats_size;
> +} __packed;
> +
> +struct scmi_msg_resp_power_domain_attributes {
> +       __le32 flags;
> +#define SUPPORTS_STATE_SET_NOTIFY(x)   ((x) & BIT(31))
> +#define SUPPORTS_STATE_SET_ASYNC(x)    ((x) & BIT(30))
> +#define SUPPORTS_STATE_SET_SYNC(x)     ((x) & BIT(29))
> +           u8 name[SCMI_MAX_STR_SIZE];
> +} __packed;

I think it would be better to leave out the __packed here, which
can lead to rather inefficient code. It's only really a problem when
building with -mstrict-align, but it's better to write code in a way that
doesn't rely on that.

> +static int
> +scmi_power_domain_attributes_get(struct scmi_handle *handle, u32 domain,
> +                                struct power_dom_info *dom_info)
> +{
> +       int ret;
> +       struct scmi_xfer *t;
> +       struct scmi_msg_resp_power_domain_attributes *attr;
> +
> +       ret = scmi_one_xfer_init(handle, POWER_DOMAIN_ATTRIBUTES,
> +                                SCMI_PROTOCOL_POWER, sizeof(domain),
> +                                sizeof(*attr), &t);
> +       if (ret)
> +               return ret;
> +
> +       *(__le32 *)t->tx.buf = cpu_to_le32(domain);
> +       attr = (struct scmi_msg_resp_power_domain_attributes *)t->rx.buf;

It seems you require a similar pattern in each caller of scmi_one_xfer_init(),
but it seems a little clumsy to always require those casts, so maybe there
is a nicer way to do this. How about making scmi_one_xfer_init() act
as an allocation function and having it return the buffer or a PTR_ERR?

It also seems odd to have it named 'init' but actually allocate the scmi_xfer
structure rather than filling a local variable that gets passed by reference.

       Arnd

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

* Re: [RFC PATCH 2/8] firmware: arm_scmi: add basic driver infrastructure for SCMI
  2017-06-07 19:18   ` Roy Franz
@ 2017-06-08  9:28     ` Sudeep Holla
  0 siblings, 0 replies; 23+ messages in thread
From: Sudeep Holla @ 2017-06-08  9:28 UTC (permalink / raw)
  To: Roy Franz
  Cc: Sudeep Holla, linux-kernel, devicetree, Roy Franz,
	Harb Abdulhamid, Nishanth Menon, Arnd Bergmann

Hi Roy,

On 07/06/17 20:18, Roy Franz wrote:
> On Wed, Jun 7, 2017 at 9:10 AM, Sudeep Holla <sudeep.holla@arm.com> wrote:
>> The SCMI is intended to allow OSPM to manage various functions that are
>> provided by the hardware platform it is running on, including power and
>> performance functions. SCMI provides two levels of abstraction, protocols
>> and transports. Protocols define individual groups of system control and
>> management messages. A protocol specification describes the messages
>> that it supports. Transports describe the method by which protocol
>> messages are communicated between agents and the platform.
>>
>> This patch adds basic infrastructure to manage the message allocation,
>> initialisation, packing/unpacking and shared memory management.
>>
>> Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
>> ---
>>  drivers/firmware/Kconfig           |  21 ++
>>  drivers/firmware/Makefile          |   1 +
>>  drivers/firmware/arm_scmi/Makefile |   2 +
>>  drivers/firmware/arm_scmi/common.h |  74 ++++
>>  drivers/firmware/arm_scmi/driver.c | 737 +++++++++++++++++++++++++++++++++++++
>>  include/linux/scmi_protocol.h      |  48 +++
>>  6 files changed, 883 insertions(+)
>>  create mode 100644 drivers/firmware/arm_scmi/Makefile
>>  create mode 100644 drivers/firmware/arm_scmi/common.h
>>  create mode 100644 drivers/firmware/arm_scmi/driver.c
>>  create mode 100644 include/linux/scmi_protocol.h
>>

[...]

>> +
>> +#define client_to_scmi_info(c) container_of(c, struct scmi_info, cl)
>> +#define handle_to_scmi_info(h) container_of(h, struct scmi_info, handle)
>> +
>> +/*
>> + * The SCP firmware only executes in little-endian mode, so any buffers
>> + * shared through SCMI should have their contents converted to little-endian
>> + */
> 
> nit:
> This really has more to do with the SCMI protocol defining everything
> as little endian, rather the endian-ness of the SCP, right?  There could be SCP
> implementations that are not Cortex M3s or little endian.
> 

Thanks for taking time to review this RFC, much appreciated. All valid
points(on this and other patches) and fixed locally now. Also thanks for
saving time in debugging these issues. I should be able to do some
testing next week.

-- 
Regards,
Sudeep

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

* Re: [RFC PATCH 6/8] firmware: arm_scmi: add initial support for power protocol
  2017-06-07 20:38   ` Arnd Bergmann
@ 2017-06-08  9:39     ` Sudeep Holla
  2017-06-08 11:06       ` Arnd Bergmann
  0 siblings, 1 reply; 23+ messages in thread
From: Sudeep Holla @ 2017-06-08  9:39 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: Sudeep Holla, Linux Kernel Mailing List, devicetree, Roy Franz,
	Harb Abdulhamid, Nishanth Menon



On 07/06/17 21:38, Arnd Bergmann wrote:
> On Wed, Jun 7, 2017 at 6:10 PM, Sudeep Holla <sudeep.holla@arm.com> wrote:
> 
>> +struct scmi_msg_resp_power_attributes {
>> +       __le16 num_domains;
>> +       __le16 reserved;
>> +       __le32 stats_addr_low;
>> +       __le32 stats_addr_high;
>> +       __le32 stats_size;
>> +} __packed;
>> +
>> +struct scmi_msg_resp_power_domain_attributes {
>> +       __le32 flags;
>> +#define SUPPORTS_STATE_SET_NOTIFY(x)   ((x) & BIT(31))
>> +#define SUPPORTS_STATE_SET_ASYNC(x)    ((x) & BIT(30))
>> +#define SUPPORTS_STATE_SET_SYNC(x)     ((x) & BIT(29))
>> +           u8 name[SCMI_MAX_STR_SIZE];
>> +} __packed;
> 
> I think it would be better to leave out the __packed here, which
> can lead to rather inefficient code. It's only really a problem when
> building with -mstrict-align, but it's better to write code in a way that
> doesn't rely on that.
> 

I assume you are referring to above structure only and not general
across all the structures ? I will have a look at this one.

>> +static int
>> +scmi_power_domain_attributes_get(struct scmi_handle *handle, u32 domain,
>> +                                struct power_dom_info *dom_info)
>> +{
>> +       int ret;
>> +       struct scmi_xfer *t;
>> +       struct scmi_msg_resp_power_domain_attributes *attr;
>> +
>> +       ret = scmi_one_xfer_init(handle, POWER_DOMAIN_ATTRIBUTES,
>> +                                SCMI_PROTOCOL_POWER, sizeof(domain),
>> +                                sizeof(*attr), &t);
>> +       if (ret)
>> +               return ret;
>> +
>> +       *(__le32 *)t->tx.buf = cpu_to_le32(domain);
>> +       attr = (struct scmi_msg_resp_power_domain_attributes *)t->rx.buf;
> 
> It seems you require a similar pattern in each caller of scmi_one_xfer_init(),
> but it seems a little clumsy to always require those casts, so maybe there
> is a nicer way to do this. How about making scmi_one_xfer_init() act
> as an allocation function and having it return the buffer or a PTR_ERR?
> 

Yes I agree it doesn't looks all nice. I have changed these few times
while developing and then thought it's better to get some suggestions. I
am open to any suggestions that will help to make these nicer.

> It also seems odd to have it named 'init' but actually allocate the scmi_xfer
> structure rather than filling a local variable that gets passed by reference.
> 

It does initialise but partially. scmi_one_xfer_get does pure allocation
while scmi_one_xfer_init initialise header variables and also tx/rx
size. But if you think it's odd, I will looks at ways to make it better.

-- 
Regards,
Sudeep

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

* Re: [RFC PATCH 6/8] firmware: arm_scmi: add initial support for power protocol
  2017-06-08  9:39     ` Sudeep Holla
@ 2017-06-08 11:06       ` Arnd Bergmann
  2017-06-08 11:14         ` Sudeep Holla
  0 siblings, 1 reply; 23+ messages in thread
From: Arnd Bergmann @ 2017-06-08 11:06 UTC (permalink / raw)
  To: Sudeep Holla
  Cc: Linux Kernel Mailing List, devicetree, Roy Franz,
	Harb Abdulhamid, Nishanth Menon

On Thu, Jun 8, 2017 at 11:39 AM, Sudeep Holla <sudeep.holla@arm.com> wrote:
>
>
> On 07/06/17 21:38, Arnd Bergmann wrote:
>> On Wed, Jun 7, 2017 at 6:10 PM, Sudeep Holla <sudeep.holla@arm.com> wrote:
>>
>>> +struct scmi_msg_resp_power_attributes {
>>> +       __le16 num_domains;
>>> +       __le16 reserved;
>>> +       __le32 stats_addr_low;
>>> +       __le32 stats_addr_high;
>>> +       __le32 stats_size;
>>> +} __packed;
>>> +
>>> +struct scmi_msg_resp_power_domain_attributes {
>>> +       __le32 flags;
>>> +#define SUPPORTS_STATE_SET_NOTIFY(x)   ((x) & BIT(31))
>>> +#define SUPPORTS_STATE_SET_ASYNC(x)    ((x) & BIT(30))
>>> +#define SUPPORTS_STATE_SET_SYNC(x)     ((x) & BIT(29))
>>> +           u8 name[SCMI_MAX_STR_SIZE];
>>> +} __packed;
>>
>> I think it would be better to leave out the __packed here, which
>> can lead to rather inefficient code. It's only really a problem when
>> building with -mstrict-align, but it's better to write code in a way that
>> doesn't rely on that.
>>
>
> I assume you are referring to above structure only and not general
> across all the structures ? I will have a look at this one.

I meant all of them, from my first look they all seem to have natural
alignment on all members anyway. If there is one that doesn't, I would
suggest annotating the individual unaligned members with __packed.

>>> +static int
>>> +scmi_power_domain_attributes_get(struct scmi_handle *handle, u32 domain,
>>> +                                struct power_dom_info *dom_info)
>>> +{
>>> +       int ret;
>>> +       struct scmi_xfer *t;
>>> +       struct scmi_msg_resp_power_domain_attributes *attr;
>>> +
>>> +       ret = scmi_one_xfer_init(handle, POWER_DOMAIN_ATTRIBUTES,
>>> +                                SCMI_PROTOCOL_POWER, sizeof(domain),
>>> +                                sizeof(*attr), &t);
>>> +       if (ret)
>>> +               return ret;
>>> +
>>> +       *(__le32 *)t->tx.buf = cpu_to_le32(domain);
>>> +       attr = (struct scmi_msg_resp_power_domain_attributes *)t->rx.buf;
>>
>> It seems you require a similar pattern in each caller of scmi_one_xfer_init(),
>> but it seems a little clumsy to always require those casts, so maybe there
>> is a nicer way to do this. How about making scmi_one_xfer_init() act
>> as an allocation function and having it return the buffer or a PTR_ERR?
>>
>
> Yes I agree it doesn't looks all nice. I have changed these few times
> while developing and then thought it's better to get some suggestions. I
> am open to any suggestions that will help to make these nicer.
>
>> It also seems odd to have it named 'init' but actually allocate the scmi_xfer
>> structure rather than filling a local variable that gets passed by reference.
>>
>
> It does initialise but partially. scmi_one_xfer_get does pure allocation
> while scmi_one_xfer_init initialise header variables and also tx/rx
> size. But if you think it's odd, I will looks at ways to make it better.

Yes, I'm still thinking about it, but I think we can do better. If a function
has both allocation and initialization parts in it, I would probably name
it *_alloc() rather than *_init().

What is the relation between scmi_one_xfer_get() and
scmi_one_xfer_init()? Do we need both in some callers, or
just one of the two?

       Arnd

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

* Re: [RFC PATCH 6/8] firmware: arm_scmi: add initial support for power protocol
  2017-06-08 11:06       ` Arnd Bergmann
@ 2017-06-08 11:14         ` Sudeep Holla
  0 siblings, 0 replies; 23+ messages in thread
From: Sudeep Holla @ 2017-06-08 11:14 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: Sudeep Holla, Linux Kernel Mailing List, devicetree, Roy Franz,
	Harb Abdulhamid, Nishanth Menon



On 08/06/17 12:06, Arnd Bergmann wrote:
> On Thu, Jun 8, 2017 at 11:39 AM, Sudeep Holla <sudeep.holla@arm.com> wrote:
>>
>>
>> On 07/06/17 21:38, Arnd Bergmann wrote:
>>> On Wed, Jun 7, 2017 at 6:10 PM, Sudeep Holla <sudeep.holla@arm.com> wrote:
>>>
>>>> +struct scmi_msg_resp_power_attributes {
>>>> +       __le16 num_domains;
>>>> +       __le16 reserved;
>>>> +       __le32 stats_addr_low;
>>>> +       __le32 stats_addr_high;
>>>> +       __le32 stats_size;
>>>> +} __packed;
>>>> +
>>>> +struct scmi_msg_resp_power_domain_attributes {
>>>> +       __le32 flags;
>>>> +#define SUPPORTS_STATE_SET_NOTIFY(x)   ((x) & BIT(31))
>>>> +#define SUPPORTS_STATE_SET_ASYNC(x)    ((x) & BIT(30))
>>>> +#define SUPPORTS_STATE_SET_SYNC(x)     ((x) & BIT(29))
>>>> +           u8 name[SCMI_MAX_STR_SIZE];
>>>> +} __packed;
>>>
>>> I think it would be better to leave out the __packed here, which
>>> can lead to rather inefficient code. It's only really a problem when
>>> building with -mstrict-align, but it's better to write code in a way that
>>> doesn't rely on that.
>>>
>>
>> I assume you are referring to above structure only and not general
>> across all the structures ? I will have a look at this one.
> 
> I meant all of them, from my first look they all seem to have natural
> alignment on all members anyway. If there is one that doesn't, I would
> suggest annotating the individual unaligned members with __packed.
> 

OK, I will take a deeper look. Thanks for the suggestion.

>>>> +static int
>>>> +scmi_power_domain_attributes_get(struct scmi_handle *handle, u32 domain,
>>>> +                                struct power_dom_info *dom_info)
>>>> +{
>>>> +       int ret;
>>>> +       struct scmi_xfer *t;
>>>> +       struct scmi_msg_resp_power_domain_attributes *attr;
>>>> +
>>>> +       ret = scmi_one_xfer_init(handle, POWER_DOMAIN_ATTRIBUTES,
>>>> +                                SCMI_PROTOCOL_POWER, sizeof(domain),
>>>> +                                sizeof(*attr), &t);
>>>> +       if (ret)
>>>> +               return ret;
>>>> +
>>>> +       *(__le32 *)t->tx.buf = cpu_to_le32(domain);
>>>> +       attr = (struct scmi_msg_resp_power_domain_attributes *)t->rx.buf;
>>>
>>> It seems you require a similar pattern in each caller of scmi_one_xfer_init(),
>>> but it seems a little clumsy to always require those casts, so maybe there
>>> is a nicer way to do this. How about making scmi_one_xfer_init() act
>>> as an allocation function and having it return the buffer or a PTR_ERR?
>>>
>>
>> Yes I agree it doesn't looks all nice. I have changed these few times
>> while developing and then thought it's better to get some suggestions. I
>> am open to any suggestions that will help to make these nicer.
>>
>>> It also seems odd to have it named 'init' but actually allocate the scmi_xfer
>>> structure rather than filling a local variable that gets passed by reference.
>>>
>>
>> It does initialise but partially. scmi_one_xfer_get does pure allocation
>> while scmi_one_xfer_init initialise header variables and also tx/rx
>> size. But if you think it's odd, I will looks at ways to make it better.
> 
> Yes, I'm still thinking about it, but I think we can do better. If a function
> has both allocation and initialization parts in it, I would probably name
> it *_alloc() rather than *_init().
> 
> What is the relation between scmi_one_xfer_get() and
> scmi_one_xfer_init()? Do we need both in some callers, or
> just one of the two?

Currently only scmi_one_xfer_init is used. Initially I was using
scmi_one_xfer_get and initialising at callsite. Strictly speaking, all
the allocations are done at probe time, it's only grabbing and releasing
one at a time at runtime, hence the name _get and _put. I can merge
_init into _get. The way it-is is just artifact of how it got developed :(
-- 
Regards,
Sudeep

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

* Re: [RFC PATCH 1/8] Documentation: add DT binding for ARM System Control and Management Interface(SCMI) protocol
  2017-06-07 16:10 ` [RFC PATCH 1/8] Documentation: add DT binding for ARM System Control and Management Interface(SCMI) protocol Sudeep Holla
@ 2017-06-09 14:16   ` Rob Herring
  2017-06-09 14:47     ` Sudeep Holla
       [not found]   ` <CAHCPf3s3MsiQyWFOgNJdD9F2JAwi_BVxVZG69zj+bJLzEw9AiA@mail.gmail.com>
  1 sibling, 1 reply; 23+ messages in thread
From: Rob Herring @ 2017-06-09 14:16 UTC (permalink / raw)
  To: Sudeep Holla
  Cc: linux-kernel, devicetree, Roy Franz, Harb Abdulhamid,
	Nishanth Menon, Arnd Bergmann, Mark Rutland

On Wed, Jun 07, 2017 at 05:10:05PM +0100, Sudeep Holla wrote:
> This patch adds devicetree binding for System Control and Management
> Interface (SCMI) Message Protocol used between the Application Cores(AP)
> and the System Control Processor(SCP). The MHU peripheral provides a
> mechanism for inter-processor communication between SCP's M3 processor
> and AP.
> 
> SCP offers control and management of the core/cluster power states,
> various power domain DVFS including the core/cluster, certain system
> clocks configuration, thermal sensors and many others.
> 
> SCMI protocol is developed as better replacement to the existing SCPI
> which is not flexible and easily extensible.
> 
> Cc: Rob Herring <robh+dt@kernel.org>
> Cc: Mark Rutland <mark.rutland@arm.com>
> Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
> ---
>  Documentation/devicetree/bindings/arm/arm,scmi.txt | 193 +++++++++++++++++++++
>  1 file changed, 193 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/arm/arm,scmi.txt
> 
> diff --git a/Documentation/devicetree/bindings/arm/arm,scmi.txt b/Documentation/devicetree/bindings/arm/arm,scmi.txt
> new file mode 100644
> index 000000000000..d6e4b7eff199
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/arm/arm,scmi.txt
> @@ -0,0 +1,193 @@
> +System Control and Management Interface (SCMI) Message Protocol
> +----------------------------------------------------------
> +
> +The SCMI is intended to allow agents such as OSPM to manage various functions
> +that are provided by the hardware platform it is running on, including power
> +and performance functions.
> +
> +This binding is intended to define the interface the firmware implementing
> +the SCMI as described in ARM document number ARM DUI 0922B ("ARM System Control
> +and Management Interface Platform Design Document")[0] provide for OSPM in
> +the device tree.
> +
> +Required properties:
> +
> +- compatible : shall be "arm,scmi"

Convince me that this genericish string is specific enough.

> +- method : The method of calling the SCMI firmware. Only permitted value
> +	   currently is:
> +	   "mailbox-doorbell" : When mailbox doorbell is used as a mechanism
> +				to alert the presence of a messages and/or
> +				notification
> +- mboxes: List of phandle and mailbox channel specifiers. It should contain
> +	  exactly one or two mailboxes, one for transmitting messages("tx")
> +	  and another optional for receiving the notifications("rx") if
> +	  supported.
> +- mbox-names: shall be "tx" or "rx"

...and optionally "rx"

> +- shmem : List of phandle pointing to the shared memory(SHM) area between the
> +	  processors using these mailboxes for IPC, one for each mailbox
> +	  SHM can be any memory reserved for the purpose of this communication
> +	  between the processors.

Maybe the mailbox binding should have a standard property for this?

> +
> +See Documentation/devicetree/bindings/mailbox/mailbox.txt for more details
> +about the generic mailbox controller and client driver bindings.
> +
> +Each protocol supported shall have a sub-node with corresponding compatible
> +as described in the following sections. If the platform supports dedicated
> +communication channel for a particular protocol, the 3 properties namely:
> +mboxes, mbox-names and shmem shall be present in the sub-node corresponding
> +to that protocol.
> +
> +Clock/Performance bindings for the clocks/OPPs based on SCMI Message Protocol
> +------------------------------------------------------------
> +
> +This binding uses the common clock binding[1].
> +
> +Required properties:
> +- compatible : shall be "arm,scmi-clocks" or "arm,scmi-perf-domains"
> +	arm,scmi-clocks:
> +	       These clocks either provide an entire range of values
> +	       between the limits or only discrete points each at fixed
> +	       step size between the limits. The firmware provides
> +	       mechanism to discover them.
> +	arm,scmi-perf-domains:
> +		These are OPPs(not just simple clocks), i.e. discrete
> +		performance levels that are supported by the platform.
> +		Again the firmware provides mechanism to discover the
> +		performance and other attributes associated with the
> +		levels.
> +
> +Other required properties for all clocks(all from common clock binding):
> +- #clock-cells : Should be 1. Contains the Clock ID value used by SCMI commands.
> +- clock-indices: The identifying number for the clocks(i.e.clock_id) in the
> +	node. It can be non linear and hence provide the mapping of
> +	identifiers.
> +
> +Power domain bindings for the power domains based on SCMI Message Protocol
> +------------------------------------------------------------
> +
> +This binding uses the generic power domain binding[4].
> +
> +PM domain providers
> +===================
> +
> +Required properties:
> + - #power-domain-cells : Should be 1. Contains the device or the power
> +			 domain ID value used by SCMI commands.
> +
> +PM domain consumers
> +===================
> +
> +Required properties:
> + - power-domains : A phandle and PM domain specifier as defined by bindings of
> +                   the power controller specified by phandle.
> +
> +Sensor bindings for the sensors based on SCMI Message Protocol
> +--------------------------------------------------------------
> +SCMI provides an API to access the various sensors on the SoC.
> +
> +Required properties:
> +- compatible : should be "arm,scmi-sensors".
> +- #thermal-sensor-cells: should be set to 1. This property follows the
> +			 thermal device tree bindings[2].
> +
> +			 Valid cell values are raw identifiers (Sensor ID)
> +			 as used by the firmware. Refer to  platform details
> +			 for your implementation for the IDs to use.
> +
> +SRAM and Shared Memory for SCMI
> +-------------------------------
> +
> +A small area of SRAM is reserved for SCMI communication between application
> +processors and SCP.
> +
> +The properties should follow the generic mmio-sram description found in [3]
> +
> +Each sub-node represents the reserved area for SCMI.
> +
> +Required sub-node properties:
> +- reg : The base offset and size of the reserved area with the SRAM
> +- compatible : should be "arm,scp-shmem" for Non-secure SRAM based
> +	       shared memory
> +
> +[0] http://infocenter.arm.com/help/topic/com.arm.doc.den0056a/index.html
> +[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
> +[2] Documentation/devicetree/bindings/thermal/thermal.txt
> +[3] Documentation/devicetree/bindings/sram/sram.txt
> +[4] Documentation/devicetree/bindings/power/power_domain.txt
> +
> +Example:
> +
> +sram: sram@50000000 {
> +	compatible = "arm,juno-sram-ns", "mmio-sram";
> +	reg = <0x0 0x50000000 0x0 0x10000>;
> +
> +	#address-cells = <1>;
> +	#size-cells = <1>;
> +	ranges = <0 0x0 0x50000000 0x10000>;
> +
> +	cpu_scp_lpri: scp-shmem@0 {
> +		compatible = "arm,juno-scp-shmem";
> +		reg = <0x0 0x200>;
> +	};
> +
> +	cpu_scp_hpri: scp-shmem@200 {
> +		compatible = "arm,juno-scp-shmem";
> +		reg = <0x200 0x200>;
> +	};
> +};
> +
> +mailbox: mailbox0@40000000 {
> +	....
> +	#mbox-cells = <1>;
> +};
> +
> +scmi_protocol: scmi@2e000000 {
> +	compatible = "arm,scmi";
> +	mboxes = <&mailbox 0 &mailbox 1>;
> +	shmem = <&cpu_scp_lpri &cpu_scp_hpri>;
> +
> +	scmi_dvfs: clocks@0 {
> +		compatible = "arm,scmi-perf-domains";
> +		#clock-cells = <1>;
> +		clock-indices = <0>, <1>, <2>;
> +	};
> +	scmi_clk: clocks@3 {
> +		compatible = "arm,scmi-clocks";
> +		#clock-cells = <1>;
> +		clock-indices = <3>, <4>;
> +	};
> +
> +	scmi_sensors: sensors {
> +		compatible = "arm,scmi-sensors";
> +		#thermal-sensor-cells = <1>;
> +	};
> +
> +	scmi_devpd: power-domains {
> +		compatible = "arm,scmi-power-domains";
> +		#power-domain-cells = <1>;
> +	};
> +};
> +
> +cpu@0 {
> +	...
> +	reg = <0 0>;
> +	clocks = <&scmi_dvfs 0>;
> +};
> +
> +hdlcd@7ff60000 {
> +	...
> +	reg = <0 0x7ff60000 0 0x1000>;
> +	clocks = <&scmi_clk 4>;
> +	power-domains = <&scmi_devpd 1>;
> +};
> +
> +thermal-zones {
> +	soc_thermal {
> +		polling-delay-passive = <100>;
> +		polling-delay = <1000>;
> +
> +				/* sensor         ID */
> +		thermal-sensors = <&scmi_sensors0 3>;
> +		...
> +	};
> +};
> -- 
> 2.7.4
> 

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

* Re: [RFC PATCH 1/8] Documentation: add DT binding for ARM System Control and Management Interface(SCMI) protocol
  2017-06-09 14:16   ` Rob Herring
@ 2017-06-09 14:47     ` Sudeep Holla
  2017-06-09 15:39       ` Rob Herring
  0 siblings, 1 reply; 23+ messages in thread
From: Sudeep Holla @ 2017-06-09 14:47 UTC (permalink / raw)
  To: Rob Herring
  Cc: Sudeep Holla, linux-kernel, devicetree, Roy Franz,
	Harb Abdulhamid, Nishanth Menon, Arnd Bergmann, Mark Rutland



On 09/06/17 15:16, Rob Herring wrote:
> On Wed, Jun 07, 2017 at 05:10:05PM +0100, Sudeep Holla wrote:
>> This patch adds devicetree binding for System Control and Management
>> Interface (SCMI) Message Protocol used between the Application Cores(AP)
>> and the System Control Processor(SCP). The MHU peripheral provides a
>> mechanism for inter-processor communication between SCP's M3 processor
>> and AP.
>>
>> SCP offers control and management of the core/cluster power states,
>> various power domain DVFS including the core/cluster, certain system
>> clocks configuration, thermal sensors and many others.
>>
>> SCMI protocol is developed as better replacement to the existing SCPI
>> which is not flexible and easily extensible.
>>
>> Cc: Rob Herring <robh+dt@kernel.org>
>> Cc: Mark Rutland <mark.rutland@arm.com>
>> Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
>> ---
>>  Documentation/devicetree/bindings/arm/arm,scmi.txt | 193 +++++++++++++++++++++
>>  1 file changed, 193 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/arm/arm,scmi.txt
>>
>> diff --git a/Documentation/devicetree/bindings/arm/arm,scmi.txt b/Documentation/devicetree/bindings/arm/arm,scmi.txt
>> new file mode 100644
>> index 000000000000..d6e4b7eff199
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/arm/arm,scmi.txt
>> @@ -0,0 +1,193 @@
>> +System Control and Management Interface (SCMI) Message Protocol
>> +----------------------------------------------------------
>> +
>> +The SCMI is intended to allow agents such as OSPM to manage various functions
>> +that are provided by the hardware platform it is running on, including power
>> +and performance functions.
>> +
>> +This binding is intended to define the interface the firmware implementing
>> +the SCMI as described in ARM document number ARM DUI 0922B ("ARM System Control
>> +and Management Interface Platform Design Document")[0] provide for OSPM in
>> +the device tree.
>> +
>> +Required properties:
>> +
>> +- compatible : shall be "arm,scmi"
> 
> Convince me that this genericish string is specific enough.
>

Now that you raised this point, I think we generate so many 4 letter
acronyms that it can collide. How about "arm,sys-ctl-mgmt-if"

>> +- method : The method of calling the SCMI firmware. Only permitted value
>> +	   currently is:
>> +	   "mailbox-doorbell" : When mailbox doorbell is used as a mechanism
>> +				to alert the presence of a messages and/or
>> +				notification
>> +- mboxes: List of phandle and mailbox channel specifiers. It should contain
>> +	  exactly one or two mailboxes, one for transmitting messages("tx")
>> +	  and another optional for receiving the notifications("rx") if
>> +	  supported.
>> +- mbox-names: shall be "tx" or "rx"
> 
> ...and optionally "rx"
> 

OK

>> +- shmem : List of phandle pointing to the shared memory(SHM) area between the
>> +	  processors using these mailboxes for IPC, one for each mailbox
>> +	  SHM can be any memory reserved for the purpose of this communication
>> +	  between the processors.
> 
> Maybe the mailbox binding should have a standard property for this?
> 

Do you mean as part of it's client binding ? If so, agreed. I can come
up with that proposal.

-- 
Regards,
Sudeep

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

* Re: [RFC PATCH 1/8] Documentation: add DT binding for ARM System Control and Management Interface(SCMI) protocol
  2017-06-09 14:47     ` Sudeep Holla
@ 2017-06-09 15:39       ` Rob Herring
  2017-06-09 15:50         ` Sudeep Holla
  0 siblings, 1 reply; 23+ messages in thread
From: Rob Herring @ 2017-06-09 15:39 UTC (permalink / raw)
  To: Sudeep Holla
  Cc: linux-kernel, devicetree, Roy Franz, Harb Abdulhamid,
	Nishanth Menon, Arnd Bergmann, Mark Rutland

On Fri, Jun 9, 2017 at 9:47 AM, Sudeep Holla <sudeep.holla@arm.com> wrote:
>
>
> On 09/06/17 15:16, Rob Herring wrote:
>> On Wed, Jun 07, 2017 at 05:10:05PM +0100, Sudeep Holla wrote:
>>> This patch adds devicetree binding for System Control and Management
>>> Interface (SCMI) Message Protocol used between the Application Cores(AP)
>>> and the System Control Processor(SCP). The MHU peripheral provides a
>>> mechanism for inter-processor communication between SCP's M3 processor
>>> and AP.
>>>
>>> SCP offers control and management of the core/cluster power states,
>>> various power domain DVFS including the core/cluster, certain system
>>> clocks configuration, thermal sensors and many others.
>>>
>>> SCMI protocol is developed as better replacement to the existing SCPI
>>> which is not flexible and easily extensible.
>>>
>>> Cc: Rob Herring <robh+dt@kernel.org>
>>> Cc: Mark Rutland <mark.rutland@arm.com>
>>> Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
>>> ---
>>>  Documentation/devicetree/bindings/arm/arm,scmi.txt | 193 +++++++++++++++++++++
>>>  1 file changed, 193 insertions(+)
>>>  create mode 100644 Documentation/devicetree/bindings/arm/arm,scmi.txt
>>>
>>> diff --git a/Documentation/devicetree/bindings/arm/arm,scmi.txt b/Documentation/devicetree/bindings/arm/arm,scmi.txt
>>> new file mode 100644
>>> index 000000000000..d6e4b7eff199
>>> --- /dev/null
>>> +++ b/Documentation/devicetree/bindings/arm/arm,scmi.txt
>>> @@ -0,0 +1,193 @@
>>> +System Control and Management Interface (SCMI) Message Protocol
>>> +----------------------------------------------------------
>>> +
>>> +The SCMI is intended to allow agents such as OSPM to manage various functions
>>> +that are provided by the hardware platform it is running on, including power
>>> +and performance functions.
>>> +
>>> +This binding is intended to define the interface the firmware implementing
>>> +the SCMI as described in ARM document number ARM DUI 0922B ("ARM System Control
>>> +and Management Interface Platform Design Document")[0] provide for OSPM in
>>> +the device tree.
>>> +
>>> +Required properties:
>>> +
>>> +- compatible : shall be "arm,scmi"
>>
>> Convince me that this genericish string is specific enough.
>>
>
> Now that you raised this point, I think we generate so many 4 letter
> acronyms that it can collide. How about "arm,sys-ctl-mgmt-if"

I was more concerned about needing versioning or vendor specific
compatible strings that we needed with SCPI. Is there a spec version
or is that discoverable?

>>> +- method : The method of calling the SCMI firmware. Only permitted value
>>> +       currently is:
>>> +       "mailbox-doorbell" : When mailbox doorbell is used as a mechanism
>>> +                            to alert the presence of a messages and/or
>>> +                            notification
>>> +- mboxes: List of phandle and mailbox channel specifiers. It should contain
>>> +      exactly one or two mailboxes, one for transmitting messages("tx")
>>> +      and another optional for receiving the notifications("rx") if
>>> +      supported.
>>> +- mbox-names: shall be "tx" or "rx"
>>
>> ...and optionally "rx"
>>
>
> OK
>
>>> +- shmem : List of phandle pointing to the shared memory(SHM) area between the
>>> +      processors using these mailboxes for IPC, one for each mailbox
>>> +      SHM can be any memory reserved for the purpose of this communication
>>> +      between the processors.
>>
>> Maybe the mailbox binding should have a standard property for this?
>>
>
> Do you mean as part of it's client binding ? If so, agreed. I can come
> up with that proposal.

Yes.

Rob

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

* Re: [RFC PATCH 1/8] Documentation: add DT binding for ARM System Control and Management Interface(SCMI) protocol
  2017-06-09 15:39       ` Rob Herring
@ 2017-06-09 15:50         ` Sudeep Holla
  0 siblings, 0 replies; 23+ messages in thread
From: Sudeep Holla @ 2017-06-09 15:50 UTC (permalink / raw)
  To: Rob Herring
  Cc: Sudeep Holla, linux-kernel, devicetree, Roy Franz,
	Harb Abdulhamid, Nishanth Menon, Arnd Bergmann, Mark Rutland



On 09/06/17 16:39, Rob Herring wrote:
> On Fri, Jun 9, 2017 at 9:47 AM, Sudeep Holla <sudeep.holla@arm.com> wrote:
>>
>>
>> On 09/06/17 15:16, Rob Herring wrote:
>>> On Wed, Jun 07, 2017 at 05:10:05PM +0100, Sudeep Holla wrote:
>>>> This patch adds devicetree binding for System Control and Management
>>>> Interface (SCMI) Message Protocol used between the Application Cores(AP)
>>>> and the System Control Processor(SCP). The MHU peripheral provides a
>>>> mechanism for inter-processor communication between SCP's M3 processor
>>>> and AP.
>>>>
>>>> SCP offers control and management of the core/cluster power states,
>>>> various power domain DVFS including the core/cluster, certain system
>>>> clocks configuration, thermal sensors and many others.
>>>>
>>>> SCMI protocol is developed as better replacement to the existing SCPI
>>>> which is not flexible and easily extensible.
>>>>
>>>> Cc: Rob Herring <robh+dt@kernel.org>
>>>> Cc: Mark Rutland <mark.rutland@arm.com>
>>>> Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
>>>> ---
>>>>  Documentation/devicetree/bindings/arm/arm,scmi.txt | 193 +++++++++++++++++++++
>>>>  1 file changed, 193 insertions(+)
>>>>  create mode 100644 Documentation/devicetree/bindings/arm/arm,scmi.txt
>>>>
>>>> diff --git a/Documentation/devicetree/bindings/arm/arm,scmi.txt b/Documentation/devicetree/bindings/arm/arm,scmi.txt
>>>> new file mode 100644
>>>> index 000000000000..d6e4b7eff199
>>>> --- /dev/null
>>>> +++ b/Documentation/devicetree/bindings/arm/arm,scmi.txt
>>>> @@ -0,0 +1,193 @@
>>>> +System Control and Management Interface (SCMI) Message Protocol
>>>> +----------------------------------------------------------
>>>> +
>>>> +The SCMI is intended to allow agents such as OSPM to manage various functions
>>>> +that are provided by the hardware platform it is running on, including power
>>>> +and performance functions.
>>>> +
>>>> +This binding is intended to define the interface the firmware implementing
>>>> +the SCMI as described in ARM document number ARM DUI 0922B ("ARM System Control
>>>> +and Management Interface Platform Design Document")[0] provide for OSPM in
>>>> +the device tree.
>>>> +
>>>> +Required properties:
>>>> +
>>>> +- compatible : shall be "arm,scmi"
>>>
>>> Convince me that this genericish string is specific enough.
>>>
>>
>> Now that you raised this point, I think we generate so many 4 letter
>> acronyms that it can collide. How about "arm,sys-ctl-mgmt-if"
> 
> I was more concerned about needing versioning or vendor specific
> compatible strings that we needed with SCPI. Is there a spec version
> or is that discoverable?
> 

Ah ok, it's discoverable and each protocol must implement
PROTOCOL_VERSION. We have specification version implemented, vendor id
and firmware implementation version all of which is mandatory.

>>>> +- method : The method of calling the SCMI firmware. Only permitted value
>>>> +       currently is:
>>>> +       "mailbox-doorbell" : When mailbox doorbell is used as a mechanism
>>>> +                            to alert the presence of a messages and/or
>>>> +                            notification
>>>> +- mboxes: List of phandle and mailbox channel specifiers. It should contain
>>>> +      exactly one or two mailboxes, one for transmitting messages("tx")
>>>> +      and another optional for receiving the notifications("rx") if
>>>> +      supported.
>>>> +- mbox-names: shall be "tx" or "rx"
>>>
>>> ...and optionally "rx"
>>>
>>
>> OK
>>
>>>> +- shmem : List of phandle pointing to the shared memory(SHM) area between the
>>>> +      processors using these mailboxes for IPC, one for each mailbox
>>>> +      SHM can be any memory reserved for the purpose of this communication
>>>> +      between the processors.
>>>
>>> Maybe the mailbox binding should have a standard property for this?
>>>
>>
>> Do you mean as part of it's client binding ? If so, agreed. I can come
>> up with that proposal.
> 
> Yes.

Thanks, will do.

-- 
Regards,
Sudeep

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

* Re: [RFC PATCH 1/8] Documentation: add DT binding for ARM System Control and Management Interface(SCMI) protocol
       [not found]   ` <CAHCPf3s3MsiQyWFOgNJdD9F2JAwi_BVxVZG69zj+bJLzEw9AiA@mail.gmail.com>
@ 2017-06-12 17:39     ` Sudeep Holla
  0 siblings, 0 replies; 23+ messages in thread
From: Sudeep Holla @ 2017-06-12 17:39 UTC (permalink / raw)
  To: Matt Sealey
  Cc: linux-kernel, devicetree, Roy Franz, Harb Abdulhamid,
	Nishanth Menon, Arnd Bergmann, Rob Herring, Mark Rutland

Hi Matt,


Thanks for starting this discussion on the list.

On Fri, Jun 09, 2017 at 01:12:50PM -0500, Matt Sealey wrote:
> Hullo all,
> 
> This is a long one.. apologies for odd linefeeds and so on.
> 
> On Wed, Jun 7, 2017 at 11:10 AM, Sudeep Holla <sudeep.holla@arm.com> wrote:
> 
> > +Clock/Performance bindings for the clocks/OPPs based on SCMI Message
> Protocol
> > +------------------------------------------------------------
> > +
> > +This binding uses the common clock binding[1].
> > +
> > +Required properties:
> > +- compatible : shall be "arm,scmi-clocks" or "arm,scmi-perf-domains"
> 
> After a little thought, there are a couple objections to be made here.
> Firstly, the SCMI protocol families are  discoverable - you only really need
> to know that it is usable (and where to use it, mboxes etc.) - whether it
> supports the clock management, performance domains, power domains et al.
> protocols is a function of querying the base protocol for a list.
>
> These protocols are identified by a value, several of which are
> standardized, some being vendor extension numbers. All protocols must be
> able to be queried for information.
>

Agreed on most the above.

> As such, defining compatible properties for each protocol is treading that
> fine line of tying device trees to particular driver subsystems and giving
> operating systems an ability to ignore any discovery procedure. While I
> can't make a case for clock management (which should obviously conform with
> a particular clock management definition in DT, as already defined), there
> is plenty of past evidence of bindings for particular devices being mis-used
> or used in non-intended ways (regulators as reset GPIOs is the one that
> immediately came to mind) in lieu of a more fleshed out way of defining a
> particular class of device for a binding. The same would be true of tying a
> 'performance domain' to the concept of clock management.
>

As I mentioned in private, I reused clock for simplicity and most of the
platforms already do that. But yes, that's no valid argument to just
continue the legacy. I am fine to break clock and performance domains.
The main problem IMO is that it's not well defined either in theis
specification or architecture to an extent that we can define a standard
binding and live with it. We are/already have seen lost of churn around
this bindings and that's one of the reason I chose to reuse existing
bindings.

> From the point of view of being able to specify things against a particular
> binding (whatever that might be), one could imagine something that mapped
> protocols to those bindings without introducing compatible names. SCMI ids
> would be verbatim, and per-protocol. Things like clock-indices are therefore
> not relevant and defining which indices go with which protocol at the SCMI
> level isn't needed anymore. It is really up to the protocol how many cells
> it would need to define it's protocol behavior but for the purpose of some
> standardization, we could imagine a binding that defined protocols as such:
> 
> scmi: arm_scmi {
>         compatible = "arm,scmi,1.0";

I prefer v1.0 dropped based on the same argument that it's discoverable.
If we don't agree on that then the whole discussion falls apart. I
assume you agree on dropping versioning just to continue the discussion
here.

>         mboxes = <y>;
>         shmem = <x>;
>         protocols {
>               scmi_clocks: protocol@0x14 {
>                         #whatever-cells = 3;
>                };
>                foo_smic: protocol@0x89 {
>                         #foo-cells = 4;
>                         #bar-cells = 5;
>                 };
>         };
> };
> 
> uart: myuart@80000000 {
>         compatible = "arm,pl011";
>         clocks = <&foo_smic 3>;
> };
> 
> If you manage to get a device tree that specifies a clock but there is no
> protocol 0x89 then you're just as hosed as if you specified an
> arm,scmi-clocks node when the protocol was not supported by SCMI itself, so
> we don't gain any new dangers, but we do gain the ability to instantiate
> SCMI, discover protocols, and then load drivers against those protocols,
> without duplicating the discovery process with a hardcoded tree. Device
> trees, from my point of view, are a contract between the SoC & board
> designer and the OS (helped along by firmware, hopefully). They shouldn't be
> dictating the driver behavior to be applied at this kind of level. 

Agreed.

> Device trees need to be rock solid - agile development is fine but as soon
> as you ship, changing the device tree means cutting off support for existing
> software, or only working with augmented features on new software and
> severely reduced functionality on old software. That can be as simple as not
> being able to go to Turbo mode, or as bad as an inability to apply thermal
> limits and burning someone's board. If we define a specific binding of a
> specific protocol to a specific way of interacting with that device which is
> a purely software construct (like treating performance domain as an abstract
> clock interface with a particular number of cells or clock-like behavior),
> then you lock down protocols to *existing* OS subsystems. That means
> maintaining that subsystem and device tree specification to retain behavior,
> which is a lot of maintaining.

In total agreement with you.

> It shouldn't be necessary to actually define which protocols exist and what
> number of cells they use in the device tree binding, because this is
> actually documented elsewhere. Just as we do not create compatible
> properties for new devices which work the same as old ones, or redefine
> features which would otherwise be ascertained by some kind of discovery (PCI
> devices, USB devices, but even as simple just reading out an ID register
> from a device to determine if it has a particular feature), we shouldn't do
> this to lock down a further discovery model for activity types or
> programmers' models under those protocols.

Reasonable.

> Here's where I fall down: with a variable number of cells per protocol
> required (potentially) and no method to be able to assign a particular
> protocol's device or functionality to something else, discovery of what maps
> where has to be done as well. There's little of that in SCMI, that is to say
> it wouldn't be possible to infer that clock_id 20 in protocol 0x14 is the
> clock that goes with cpu@0. It is also, per the SCMI spec, a firmware table
> responsibility to define any dependencies on other parts of the protocol
> (for instance, clock trees). The best I can think of right now is that this
> would just have to be done on a global SCMI level:

> 
> scmi: arm_scmi {
>         compatible = "arm,scmi,1.0";
>         mboxes = <y>;
>         shmem = <x>;
>         disable-protocols = <0x87, 0x88>;
>	  // these are buggy, don't load drivers even if they're implemented
> 
>         #whatever-cells = 3;
>         required-whatever-prop;
> 
>         #foo-cells = 4;
> 
>         #bar-cells = 5;
>         optional-bar-prop = <5, 10, 15>;
> 
>         #clock-cells = 10;
> };
> 
> #define SCMI_PROTO_CLOCKMGMT 0x14
> #define SCMI_PROTO_VENDOR_CLOCKS 0x89
> uart: myuart {
>         compatible = "arm,pl011";
>         clocks = <&scmi SCMI_PROTO_CLOCKMGMT 3 0 0 0 0 0 0 0 0>, <&scmi
> SCMIU_PROTO_VENDOR_CLOCKS 3 4 7 99 0 0 0x9000 3>;
> };
> 

OK, I really don't like this. But if other's think this is better, then
I am fine with that. I prefer the first option you have present.

IMO we might collide with the property name soon i.e. 2 different
existing bindings having same property name.

> .. it is up to the driver to figure out what exactly this does in real
> life, without having to lock it to the clock et al. binding, but at least
> all drivers using protocols must be able to parse the number of cells
> defined. If a protocol only needs 3 cells, but another needs 10 cells for
> the same thing, then both protocols will by definition be defined as 10-cell
> groups. It implies hat any device that can be controlled or affected by SCMI
> can list the devices, and the protocol driver will be required to parse the
> remaining cells and ignore them. As long as the device tree can cover all
> cases in it's #foo-cells or other binding properties, it would be most
> flexible here.
>
> I don't like the lockdown of having to cover every binding that gets used
> whether it's truly for that device type or not, but it would be the most
> flexible within the current framework, without defeating discovery.

Sounds good, but as I said I am worried if we collide.

> We would do well to come up with a way of defining abstract interfaces to
> firmware or other processors that don't rely on there being a fixed binding
> that dictates what kind of device it is, where there isn't a way of defining
> that device type in a generic manner. There's no way of doing this right now
> and letting the driver in the OS know what's necessary - well, there is,
> things like RTAS support on CHRP got away with this in the old days, and
> PSCI does something very similar (which covers quite a lot of CPU management
> and also system domain activity), so it's not like we've come up against the
> issue before. But it's not been resolved when a device node would need to
> refer back to those abstraction interface nodes where there's not a
> reasonable way of binding the definition of the reference.

Exactly, I am trying to reuse existing bindings with minimum change to
provide the glue to SCMI interface. But I am open for any suggestions.

> RTAS had a property in every device node that depended on it and would be
> affected by it, "used-by-rtas". Perhaps we could augment devices with an
> "managed-by" property likewise to point to the protocol node.
> #protocol-cells might be a nice way of defining cell sizes generically
> (where protocol would be the name of the protocol - #scmi-cells perhaps -
> and the format of #scmi-cells would need to include the protocol number).
> Wrapping that up in a generic binding means each protocol then gets control
> of it's own world, and each device that is affected by that protocol would
> be able to realize this.

Interesting, I need more opinions here.

> If anyone comes up with a particular PSCI-like protocol for anything here
> that kind of adaptable binding for device/platform abstraction frameworks
> with non-discrete methods of working (i.e. it might not be able to be
> defined as "just a clock" or "just a power domain" or "just a thermal
> framework" or any combination without reducing functionality that would
> otherwise come from some built-in discovery system) would come in very
> handy.
>
> Just thoughts right now, though. I definitely don't have the whole story
> down, but it is definitely something to think about.

Sure, thanks again for such a detailed mail. Hope to see more
discussions if we need to make such drastic changes.

-- 
Regards,
Sudeep

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

end of thread, other threads:[~2017-06-12 17:39 UTC | newest]

Thread overview: 23+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-06-07 16:10 [RFC PATCH 0/8] firmware: ARM System Control and Management Interface(SCMI) support Sudeep Holla
2017-06-07 16:10 ` [RFC PATCH 1/8] Documentation: add DT binding for ARM System Control and Management Interface(SCMI) protocol Sudeep Holla
2017-06-09 14:16   ` Rob Herring
2017-06-09 14:47     ` Sudeep Holla
2017-06-09 15:39       ` Rob Herring
2017-06-09 15:50         ` Sudeep Holla
     [not found]   ` <CAHCPf3s3MsiQyWFOgNJdD9F2JAwi_BVxVZG69zj+bJLzEw9AiA@mail.gmail.com>
2017-06-12 17:39     ` Sudeep Holla
2017-06-07 16:10 ` [RFC PATCH 2/8] firmware: arm_scmi: add basic driver infrastructure for SCMI Sudeep Holla
2017-06-07 19:18   ` Roy Franz
2017-06-08  9:28     ` Sudeep Holla
2017-06-07 16:10 ` [RFC PATCH 3/8] firmware: arm_scmi: add common infrastructure and support for base protocol Sudeep Holla
2017-06-07 19:19   ` Roy Franz
2017-06-07 16:10 ` [RFC PATCH 4/8] firmware: arm_scmi: add initial support for performance protocol Sudeep Holla
2017-06-07 16:10 ` [RFC PATCH 5/8] firmware: arm_scmi: add initial support for clock protocol Sudeep Holla
2017-06-07 19:19   ` Roy Franz
2017-06-07 16:10 ` [RFC PATCH 6/8] firmware: arm_scmi: add initial support for power protocol Sudeep Holla
2017-06-07 20:38   ` Arnd Bergmann
2017-06-08  9:39     ` Sudeep Holla
2017-06-08 11:06       ` Arnd Bergmann
2017-06-08 11:14         ` Sudeep Holla
2017-06-07 16:10 ` [RFC PATCH 7/8] firmware: arm_scmi: add initial support for sensor protocol Sudeep Holla
2017-06-07 19:19   ` Roy Franz
2017-06-07 16:10 ` [RFC PATCH 8/8] firmware: arm_scmi: probe and initialise all the supported protocols Sudeep Holla

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).