From patchwork Mon Jun 26 15:55:06 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sudeep Holla X-Patchwork-Id: 106355 Delivered-To: patch@linaro.org Received: by 10.140.101.48 with SMTP id t45csp166488qge; Mon, 26 Jun 2017 08:55:30 -0700 (PDT) X-Received: by 10.98.79.29 with SMTP id d29mr784903pfb.57.1498492530424; Mon, 26 Jun 2017 08:55:30 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1498492530; cv=none; d=google.com; s=arc-20160816; b=Zxzvx+8ujxC0ouCmYCJ7cG8G00W017hoZYCtUqvGBLNNyBbGmvAXz+wwk0PCocCYAo LjXxhq55GJahrmD9RNWdC8eTs7NAXl0jwTmarQ1EiU6LFHE7LJ0eJw1zi0uGA5q8YYjZ NKIfCKl8D9apFMzaHthraJWKaBnIR8kj9Gd2odDRH1Ys2JWdWWZ906hd36pxCQIIh7Kj dhTIbNhdVjN+mfWwlPBHfh9XeLK3HG7+dFU0EGO8kjb7zE/vJw6txHsxKCYsE66lkp8H Gc/1krY5kGJG2LbaD8ZS/tUwYOZOAJ2wGggVJPjRM8WB7dls3HXqmOge46XLVmX79lfR 1wEQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:arc-authentication-results; bh=lgMsgoaUnEVZurPkx2rVoIH8ACfBw6oqGRw86/kEGg0=; b=RMEbpoNXANbecjbAhCE1QD9M8ODOmh9vQIpZSRx5sibTzgBgduapY9PrjBNWVQf0Nv IxvVZwsT867hP1K2pgve8cJUqpWyCYv1GhjeA03x9HOGgJ4k841BpdkPvE9JlhUYsAuJ R5EuoZ04sDBl4f9xRfPEr94AYTH6ipLeQLueN+UJxnBDvOGid1ydI6WJhxwH5v4k45OQ YYIrJeH6n69sdZyArGmAnPECjZVukP5oUeuC38d7UfHcUOeHXfkNM8UjYqv5Q3F+mA8f i5QtLvMsWQhZ3qd7AcW1wKbzurd7vgm5mPLWQO3EdouEMOlRHzxtn3odRHLVBXuGuwl5 r/Ug== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of devicetree-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=devicetree-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id i6si224401pgt.488.2017.06.26.08.55.30; Mon, 26 Jun 2017 08:55:30 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of devicetree-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of devicetree-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=devicetree-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752138AbdFZPz2 (ORCPT + 7 others); Mon, 26 Jun 2017 11:55:28 -0400 Received: from foss.arm.com ([217.140.101.70]:46024 "EHLO foss.arm.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752105AbdFZPz0 (ORCPT ); Mon, 26 Jun 2017 11:55:26 -0400 Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.72.51.249]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 9975915B2; Mon, 26 Jun 2017 08:55:25 -0700 (PDT) Received: from e107155-lin.cambridge.arm.com (e107155-lin.cambridge.arm.com [10.1.211.34]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPA id BC1B53F4FF; Mon, 26 Jun 2017 08:55:23 -0700 (PDT) From: Sudeep Holla To: ALKML , LKML , DTML Cc: Sudeep Holla , Roy Franz , Harb Abdulhamid , Nishanth Menon , Arnd Bergmann , Loc Ho , Alexey Klimov , Matt Sealey , Rob Herring Subject: [PATCH 2/9] Documentation: add DT binding for ARM System Control and Management Interface(SCMI) protocol Date: Mon, 26 Jun 2017 16:55:06 +0100 Message-Id: <1498492513-29771-3-git-send-email-sudeep.holla@arm.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1498492513-29771-1-git-send-email-sudeep.holla@arm.com> References: <1498492513-29771-1-git-send-email-sudeep.holla@arm.com> Sender: devicetree-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: devicetree@vger.kernel.org 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 Signed-off-by: Sudeep Holla --- Documentation/devicetree/bindings/arm/arm,scmi.txt | 175 +++++++++++++++++++++ 1 file changed, 175 insertions(+) create mode 100644 Documentation/devicetree/bindings/arm/arm,scmi.txt -- 2.7.4 -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html diff --git a/Documentation/devicetree/bindings/arm/arm,scmi.txt b/Documentation/devicetree/bindings/arm/arm,scmi.txt new file mode 100644 index 000000000000..d4565e78a8d7 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/arm,scmi.txt @@ -0,0 +1,175 @@ +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 as per + generic mailbox client binding. + +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: +- #clock-cells : Should be 1. Contains the Clock ID value used by SCMI commands. + +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: +- #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"; + method = "mailbox-doorbell"; + mboxes = <&mailbox 0 &mailbox 1>; + shmem = <&cpu_scp_lpri &cpu_scp_hpri>; + #address-cells = <1>; + #size-cells = <0>; + + scmi_devpd: protocol@11 { + reg = <0x11>; + #power-domain-cells = <1>; + }; + + scmi_dvfs: protocol@13 { + reg = <0x13>; + #clock-cells = <1>; + }; + + scmi_clk: protocol@14 { + reg = <0x14>; + #clock-cells = <1>; + }; + + scmi_sensors0: protocol@15 { + reg = <0x15>; + #thermal-sensor-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>; + ... + }; +}; From patchwork Mon Jun 26 15:55:07 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sudeep Holla X-Patchwork-Id: 106357 Delivered-To: patch@linaro.org Received: by 10.140.101.48 with SMTP id t45csp166711qge; Mon, 26 Jun 2017 08:55:49 -0700 (PDT) X-Received: by 10.99.43.5 with SMTP id r5mr759321pgr.135.1498492549578; Mon, 26 Jun 2017 08:55:49 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1498492549; cv=none; d=google.com; s=arc-20160816; b=lJ5VcZLpi2eC5RKgniKv7fRbtCordTshhxBsgJHTJR3dsryLURP8uH57DirDzjdsuV 8awzNmArV9EJRiEKlZsN/ZxNR3pA5LYQW+Z36JXTU4NhoTNz9GaFGST8pyAUjNR4y1eM Ii3Ap6Aa6yPRhite5Zt01RXBRH0pQ6+wnyUnEg7Om53SsSASv+gHrWUfQJXdhsMmPbet Ba522YiJh9yg6e/I98i/V2jBFj+cSjC0bhtKDa/vP2T7f9VIGQxcC2ITifcf6W00QacH RJSYs3kh0qKC73UG33nVXZEaDztGfOmrsuaJHJ4GKaNH7CLmzdX5E32Z8m1I5WIaACK5 6zTA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:arc-authentication-results; bh=XAXHzpuL9egCSidBrk4dLoovOPQTRnBLOjt/ika99k0=; b=D1ocOjjpiAxgF6rDRBGbZsZXY7M/+JrJHFhTfe1avp+XVgaY77sBDOHsm9nEn26nGP 41TNYkN7UXQNETv+NVkhjh0chYxcfY4uQaXjGFnOlvLvYuuGkb88FfU5IDqaUYQhJKe9 KoG0Rh9FtA64P+PAQXInbWTTWaE0vzdFkLyjaVGmXsS1ppN2V5gH2x/MrwaogNxaNxJH N1ZigAIDah/RjQyLmgsMX3dHJhY5d8EQXz2NuApClUgH90tsc1ybLXiFZFl6mC+Vq/yK EqnsRWJADywvVtihRAE7nQj+azGJSlJ1MQKXvwyQot6DxAcpz9t6SbLdefKuysxXnJv1 rq+Q== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of devicetree-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=devicetree-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id k1si266516pld.526.2017.06.26.08.55.49; Mon, 26 Jun 2017 08:55:49 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of devicetree-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of devicetree-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=devicetree-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752165AbdFZPzb (ORCPT + 7 others); Mon, 26 Jun 2017 11:55:31 -0400 Received: from foss.arm.com ([217.140.101.70]:46032 "EHLO foss.arm.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752133AbdFZPz2 (ORCPT ); Mon, 26 Jun 2017 11:55:28 -0400 Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.72.51.249]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 94A6F15BE; Mon, 26 Jun 2017 08:55:27 -0700 (PDT) Received: from e107155-lin.cambridge.arm.com (e107155-lin.cambridge.arm.com [10.1.211.34]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPA id D6AF73F4FF; Mon, 26 Jun 2017 08:55:25 -0700 (PDT) From: Sudeep Holla To: ALKML , LKML , DTML Cc: Sudeep Holla , Roy Franz , Harb Abdulhamid , Nishanth Menon , Arnd Bergmann , Loc Ho , Alexey Klimov , Matt Sealey Subject: [PATCH 3/9] firmware: arm_scmi: add basic driver infrastructure for SCMI Date: Mon, 26 Jun 2017 16:55:07 +0100 Message-Id: <1498492513-29771-4-git-send-email-sudeep.holla@arm.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1498492513-29771-1-git-send-email-sudeep.holla@arm.com> References: <1498492513-29771-1-git-send-email-sudeep.holla@arm.com> Sender: devicetree-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: devicetree@vger.kernel.org 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 --- 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 | 730 +++++++++++++++++++++++++++++++++++++ include/linux/scmi_protocol.h | 48 +++ 6 files changed, 876 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 -- 2.7.4 -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html 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..a6829afc17e3 --- /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 . + */ + +#include +#include +#include + +/** + * 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 + * + * @buf: Buffer pointer + * @len: Length of data in the Buffer + */ +struct scmi_msg { + void *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_one_xfer_put(const struct scmi_handle *h, struct scmi_xfer *xfer); +int scmi_do_xfer(const struct scmi_handle *h, struct scmi_xfer *xfer); +int scmi_one_xfer_init(const struct scmi_handle *h, u8 msg_id, u8 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..0fda87a7ec72 --- /dev/null +++ b/drivers/firmware/arm_scmi/driver.c @@ -0,0 +1,730 @@ +/* + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 providing SCM interface to OSPM and other agents must + * execute only in little-endian mode as per SCMI specification, 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]; +}; + +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 %zu 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(const 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); + if (bit_pos == info->desc->max_msg) { + spin_unlock_irqrestore(&minfo->xfer_lock, flags); + return ERR_PTR(-ENOMEM); + } + 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_one_xfer_put() - 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_one_xfer_put(const struct scmi_handle *handle, struct scmi_xfer *xfer) +{ + unsigned long flags; + struct scmi_info *info = handle_to_scmi_info(handle); + struct scmi_xfers_info *minfo = &info->minfo; + + /* + * 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->hdr.seq, 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(const 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(const struct scmi_handle *handle, u8 msg_id, u8 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 = 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_handle_put 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 scmi_handle *handle = NULL; + + if (!dev || !dev->parent) { + pr_err("missing device or parent pointer\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 (dev->parent == info->dev) { + handle = &info->handle; + info->users++; + break; + } + } + mutex_unlock(&scmi_list_mutex); + + if (!handle) + return ERR_PTR(-EPROBE_DEFER); + + return handle; +} +EXPORT_SYMBOL_GPL(scmi_handle_get); + +/** + * scmi_handle_put() - 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_handle_put 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_handle_put(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_handle_put); + +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_handle_put(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_handle_put. + * 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(u8), 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 0; +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 "); +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..fbe80fa1ec9f --- /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 . + */ +#include + +/** + * 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_handle_put(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_handle_put(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 */ From patchwork Mon Jun 26 15:55:09 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sudeep Holla X-Patchwork-Id: 106356 Delivered-To: patch@linaro.org Received: by 10.140.101.48 with SMTP id t45csp166714qge; Mon, 26 Jun 2017 08:55:49 -0700 (PDT) X-Received: by 10.84.140.107 with SMTP id 98mr880833pls.97.1498492549911; Mon, 26 Jun 2017 08:55:49 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1498492549; cv=none; d=google.com; s=arc-20160816; b=Gm3kxBYYHVTF5AdiLyaiq2vDDj9vo+Z7d8upsLp20W7F7tr4iloPl42U13vwGjatQg XDuPbLO1pLGsZQsw/szjNnI+uQz9DeRU42YmxclDvVhRDHiG6EJQYYj6hl0XgJyilHpV 0n6VW7wVZMMqjGhj45WPbc4nCSdZR2SjxFaXRSk5AVXHWgD/DH8QZT/YcNapBrX8/t0J iqQdxyX6xQsE2t6AotEDRo1V/o3BhvsSlnP4lpEDsqU5herpY+viym0uGXpFaCcEsan7 M4PuF8NNq7pSPpFmktc1D1nS2DnzlVvq/wCX+x5/9ARCXVX8Qw9dG5J28NFXMr5hmqbj 38VA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:arc-authentication-results; bh=JFQRNVHIEvbCqRU8T+VPoFsKV2eQHSYB62yr1pnedL8=; b=ESOzjQQ9gEz1jdN7bpa8zQST6Fg18CjYP5nFQxrhALhpYQGO1T8CoHRCfcD3Bj/lCc CxHLoLKewJKcy8KpsjC1RMbxB6YkY/sGRMp8qCEGB7RTRoNtQHWC2BhBFRfzqrBYzt8W sd7gtlkztBmWq90ORaSYQsPpsVmhrVY4PghWuVZkua7lHZ7SHIsUcYGnEvZ6IF3pDX+J XV4AXy1aWk1heYebbDKC5qPMXZbEWq3odw9VcqI9zvB6ySi2Oz0t8A8aweMFsNnEIOvH ngXT/WhOcCFxMV9jelhK6eJo/HrW81JYdU3YtRgMh71wbuCWSC1mbeZMwCWYLMqabjX7 q6Sg== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of devicetree-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=devicetree-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id k1si266516pld.526.2017.06.26.08.55.49; Mon, 26 Jun 2017 08:55:49 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of devicetree-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of devicetree-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=devicetree-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752172AbdFZPze (ORCPT + 7 others); Mon, 26 Jun 2017 11:55:34 -0400 Received: from foss.arm.com ([217.140.101.70]:46070 "EHLO foss.arm.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751402AbdFZPzc (ORCPT ); Mon, 26 Jun 2017 11:55:32 -0400 Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.72.51.249]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 85D001610; Mon, 26 Jun 2017 08:55:31 -0700 (PDT) Received: from e107155-lin.cambridge.arm.com (e107155-lin.cambridge.arm.com [10.1.211.34]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPA id C9BAA3F4FF; Mon, 26 Jun 2017 08:55:29 -0700 (PDT) From: Sudeep Holla To: ALKML , LKML , DTML Cc: Sudeep Holla , Roy Franz , Harb Abdulhamid , Nishanth Menon , Arnd Bergmann , Loc Ho , Alexey Klimov , Matt Sealey Subject: [PATCH 5/9] firmware: arm_scmi: add initial support for performance protocol Date: Mon, 26 Jun 2017 16:55:09 +0100 Message-Id: <1498492513-29771-6-git-send-email-sudeep.holla@arm.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1498492513-29771-1-git-send-email-sudeep.holla@arm.com> References: <1498492513-29771-1-git-send-email-sudeep.holla@arm.com> Sender: devicetree-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: devicetree@vger.kernel.org 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 --- drivers/firmware/arm_scmi/Makefile | 2 +- drivers/firmware/arm_scmi/common.h | 1 + drivers/firmware/arm_scmi/perf.c | 511 +++++++++++++++++++++++++++++++++++++ include/linux/scmi_protocol.h | 31 +++ 4 files changed, 544 insertions(+), 1 deletion(-) create mode 100644 drivers/firmware/arm_scmi/perf.c -- 2.7.4 -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html 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 e3fe5d9acc82..7473dfcad4ee 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..1cb4ba705094 --- /dev/null +++ b/drivers/firmware/arm_scmi/perf.c @@ -0,0 +1,511 @@ +/* + * 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 . + */ + +#include +#include +#include +#include + +#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_opp { + u32 perf; + u32 power; + u32 trans_latency_us; +}; + +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; +}; + +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)) + __le32 rate_limit_us; + __le32 sustained_freq_khz; + __le32 sustained_perf_level; + u8 name[SCMI_MAX_STR_SIZE]; +}; + +struct scmi_msg_perf_describe_levels { + __le32 domain; + __le32 level_index; +}; + +struct scmi_perf_set_limits { + __le32 domain; + __le32 max_level; + __le32 min_level; +}; + +struct scmi_perf_get_limits { + __le32 max_level; + __le32 min_level; +}; + +struct scmi_perf_set_level { + __le32 domain; + __le32 level; +}; + +struct scmi_perf_notify_level_or_limits { + __le32 domain; + __le32 notify_enable; +}; + +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]; +}; + +struct perf_dom_info { + bool set_limits; + bool set_perf; + bool perf_limit_notify; + bool perf_level_notify; + u32 opp_count; + u32 sustained_freq_khz; + u32 sustained_perf_level; + u32 mult_factor; + 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(const 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 = 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_one_xfer_put(handle, t); + return ret; +} + +static int +scmi_perf_domain_attributes_get(const 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 = 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); + dom_info->sustained_freq_khz = + le32_to_cpu(attr->sustained_freq_khz); + dom_info->sustained_perf_level = + le32_to_cpu(attr->sustained_perf_level); + dom_info->mult_factor = (dom_info->sustained_freq_khz * 1000) / + dom_info->sustained_perf_level; + memcpy(dom_info->name, attr->name, SCMI_MAX_STR_SIZE); + } + + scmi_one_xfer_put(handle, t); + return ret; +} + +static int opp_cmp_func(const void *opp1, const void *opp2) +{ + const struct scmi_opp *t1 = opp1, *t2 = opp2; + + return t1->perf - t2->perf; +} + +static int +scmi_perf_describe_levels_get(const 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 = t->tx.buf; + level_info = 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->perf = le32_to_cpu(level_info->opp[cnt].perf_val); + opp->power = 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->perf, opp->power, 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); + + perf_dom->opp_count = tot_opp_cnt; + scmi_one_xfer_put(handle, t); + + sort(perf_dom->opp, tot_opp_cnt, sizeof(*opp), opp_cmp_func, NULL); + return ret; +} + +static int scmi_perf_limits_set(const 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 = 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_one_xfer_put(handle, t); + return ret; +} + +static int scmi_perf_limits_get(const 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 = t->rx.buf; + + *max_perf = le32_to_cpu(limits->max_level); + *min_perf = le32_to_cpu(limits->min_level); + } + + scmi_one_xfer_put(handle, t); + return ret; +} + +static int +scmi_perf_level_set(const 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 = t->tx.buf; + lvl->domain = cpu_to_le32(domain); + lvl->level = cpu_to_le32(level); + + ret = scmi_do_xfer(handle, t); + + scmi_one_xfer_put(handle, t); + return ret; +} + +static int +scmi_perf_level_get(const 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_one_xfer_put(handle, t); + return ret; +} + +static int __scmi_perf_notify_enable(const 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 = 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_one_xfer_put(handle, t); + return ret; +} + +static int scmi_perf_limits_notify_enable(const struct scmi_handle *handle, + u32 domain, bool enable) +{ + return __scmi_perf_notify_enable(handle, PERF_NOTIFY_LIMITS, + domain, enable); +} + +static int scmi_perf_level_notify_enable(const struct scmi_handle *handle, + u32 domain, bool enable) +{ + return __scmi_perf_notify_enable(handle, PERF_NOTIFY_LEVEL, + domain, enable); +} + +/* Device specific ops */ +static int scmi_dev_domain_id(struct device *dev) +{ + struct of_phandle_args clkspec; + + if (of_parse_phandle_with_args(dev->of_node, "clocks", "#clock-cells", + 0, &clkspec)) + return -EINVAL; + + return clkspec.args[0]; +} + +static int scmi_dvfs_add_opps_to_device(struct device *dev) +{ + int idx, ret, domain; + unsigned long freq; + struct scmi_opp *opp; + struct perf_dom_info *dom; + + domain = scmi_dev_domain_id(dev); + if (domain < 0) + return domain; + + dom = perf_info.dom_info + domain; + if (!dom) + return -EIO; + + for (opp = dom->opp, idx = 0; idx < dom->opp_count; idx++, opp++) { + freq = opp->perf * dom->mult_factor; + + ret = dev_pm_opp_add(dev, freq, opp->power); + if (ret) { + dev_warn(dev, "failed to add opp %luHz %umV\n", + freq, opp->power); + + while (idx-- > 0) { + freq = (--opp)->perf * dom->mult_factor; + dev_pm_opp_remove(dev, freq); + } + return ret; + } + } + return 0; +} + +static int scmi_dvfs_get_transition_latency(struct device *dev) +{ + struct perf_dom_info *dom; + int domain = scmi_dev_domain_id(dev); + + if (domain < 0) + return domain; + + dom = perf_info.dom_info + domain; + if (!dom) + return -EIO; + + return dom->opp[dom->opp_count - 1].trans_latency_us; +} + +static int scmi_dvfs_freq_set(const struct scmi_handle *handle, u32 domain, + unsigned long freq) +{ + struct perf_dom_info *dom = perf_info.dom_info + domain; + + return scmi_perf_level_set(handle, domain, freq / dom->mult_factor); +} + +static int scmi_dvfs_freq_get(const struct scmi_handle *handle, u32 domain, + unsigned long *freq) +{ + int ret; + u32 level; + struct perf_dom_info *dom = perf_info.dom_info + domain; + + ret = scmi_perf_level_get(handle, domain, &level); + if (!ret) + *freq = level * dom->mult_factor; + + return ret; +} + +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, + .device_domain_id = scmi_dev_domain_id, + .get_transition_latency = scmi_dvfs_get_transition_latency, + .add_opps_to_device = scmi_dvfs_add_opps_to_device, + .freq_set = scmi_dvfs_freq_set, + .freq_get = scmi_dvfs_freq_get, +}; + +int scmi_perf_protocol_init(struct scmi_handle *handle) +{ + int domain; + u32 version; + + 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 3ef2d48f03c2..c9f97e69444a 100644 --- a/include/linux/scmi_protocol.h +++ b/include/linux/scmi_protocol.h @@ -43,15 +43,46 @@ 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)(const struct scmi_handle *, u32, u32, u32); + int (*limits_get)(const struct scmi_handle *, u32, u32 *, u32 *); + int (*level_set)(const struct scmi_handle *, u32, u32); + int (*level_get)(const struct scmi_handle *, u32, u32 *); + int (*limits_notify_enable)(const struct scmi_handle *, u32, bool); + int (*level_notify_enable)(const struct scmi_handle *, u32, bool); + int (*device_domain_id)(struct device *); + int (*get_transition_latency)(struct device *); + int (*add_opps_to_device)(struct device *); + int (*freq_set)(const struct scmi_handle *, u32, unsigned long); + int (*freq_get)(const struct scmi_handle *, u32, unsigned long *); +}; + /** * 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; }; #if IS_REACHABLE(CONFIG_ARM_SCMI_PROTOCOL) From patchwork Mon Jun 26 15:55:10 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sudeep Holla X-Patchwork-Id: 106359 Delivered-To: patch@linaro.org Received: by 10.140.101.48 with SMTP id t45csp166727qge; Mon, 26 Jun 2017 08:55:50 -0700 (PDT) X-Received: by 10.98.149.198 with SMTP id c67mr803984pfk.134.1498492550733; Mon, 26 Jun 2017 08:55:50 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1498492550; cv=none; d=google.com; s=arc-20160816; b=G2tAJVdQniUFJF2yn24Peh8YAeKxXPom0jcyVnUoJ1O6R/8bK2/9cUfLMS9Dfqevho SqVEuCkU0yP0V/OmftiN3Yq4whX6v5lrJsK+T5YkdJ4bxGUlYipo/l7HlS6AO9Of/z5a 2RwIC7K9XEQapK046w5e6jYQeO0iud9gAX3/4RfVkRHuXFRMvOpWmyn/hggvVO80uyc1 X+GaoewtHr55P3L2dNjbcwgsbpZ7B3DGDDlxQBo7qznvYhFvJuwgobUaAQQL2ePA+Hvu Mvl6vdj6br3OkH3hNZtpo9WZvFblXTeB7+MpLWRzj5Vn9ZaW1tmjPNOYJL0a1c22+Ns4 XAaw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:arc-authentication-results; bh=RQlMvVdowvHCRbE9htyoOLyXdWpnd7fNWUa6t04kTic=; b=YfZPzRIgGn6MMXDMziCs/Bnvw/11vWrrvQmDR+c1hFJAcNN2HxIN7BN6Mhgc5l3v01 W+8TArDDkFQWiLuQfEIow3mBfMyfI7NVdfBRfIWWkWu7Lrp2to1Y132EfiRvXRPfXSBh nQrRBdukKfjQPYkFt7S+6jhSSru894iuyCHuT1schkqFfXlJA6pujtaDDyIg2tDddPsF 9v8leozZQ2/qnDr24KGVlv06G2VN5To+Z4iO4m5FmfzIPSXXl+oqCKXJKAHreTX2IF7Y B4p7/NMDrmvys23B6BAcNuKR752+kdh6t6mHOh2vonyHC+ktbvxFa7GBbu7E+C1TX/bJ qeGQ== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of devicetree-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=devicetree-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id k1si266516pld.526.2017.06.26.08.55.50; Mon, 26 Jun 2017 08:55:50 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of devicetree-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of devicetree-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=devicetree-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752294AbdFZPzl (ORCPT + 7 others); Mon, 26 Jun 2017 11:55:41 -0400 Received: from foss.arm.com ([217.140.101.70]:46088 "EHLO foss.arm.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752252AbdFZPzj (ORCPT ); Mon, 26 Jun 2017 11:55:39 -0400 Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.72.51.249]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 805EA1650; Mon, 26 Jun 2017 08:55:33 -0700 (PDT) Received: from e107155-lin.cambridge.arm.com (e107155-lin.cambridge.arm.com [10.1.211.34]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPA id C1EA63F4FF; Mon, 26 Jun 2017 08:55:31 -0700 (PDT) From: Sudeep Holla To: ALKML , LKML , DTML Cc: Sudeep Holla , Roy Franz , Harb Abdulhamid , Nishanth Menon , Arnd Bergmann , Loc Ho , Alexey Klimov , Matt Sealey Subject: [PATCH 6/9] firmware: arm_scmi: add initial support for clock protocol Date: Mon, 26 Jun 2017 16:55:10 +0100 Message-Id: <1498492513-29771-7-git-send-email-sudeep.holla@arm.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1498492513-29771-1-git-send-email-sudeep.holla@arm.com> References: <1498492513-29771-1-git-send-email-sudeep.holla@arm.com> Sender: devicetree-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: devicetree@vger.kernel.org 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 --- drivers/firmware/arm_scmi/Makefile | 2 +- drivers/firmware/arm_scmi/clock.c | 339 +++++++++++++++++++++++++++++++++++++ include/linux/scmi_protocol.h | 40 +++++ 3 files changed, 380 insertions(+), 1 deletion(-) create mode 100644 drivers/firmware/arm_scmi/clock.c -- 2.7.4 -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html 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..87d0befab07d --- /dev/null +++ b/drivers/firmware/arm_scmi/clock.c @@ -0,0 +1,339 @@ +/* + * 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 . + */ + +#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; +}; + +struct scmi_msg_resp_clock_attributes { + __le32 attributes; +#define CLOCK_ENABLE BIT(0) + u8 name[SCMI_MAX_STR_SIZE]; +}; + +struct scmi_clock_set_config { + __le32 id; + __le32 attributes; +}; + +struct scmi_msg_clock_describe_rates { + __le32 id; + __le32 rate_index; +}; + +struct scmi_msg_resp_clock_describe_rates { + __le32 num_rates_flags; +#define NUM_RETURNED(x) ((x) & 0xfff) +#define RATE_DISCRETE(x) !((x) & BIT(12)) +#define NUM_REMAINING(x) ((x) >> 16) + 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; \ +}) +}; + +struct scmi_clock_set_rate { + __le32 flags; +#define CLOCK_SET_ASYNC BIT(0) +#define CLOCK_SET_DELAYED BIT(1) +#define CLOCK_SET_ROUND_UP BIT(2) +#define CLOCK_SET_ROUND_AUTO BIT(3) + __le32 id; + __le32 value_low; + __le32 value_high; +}; + +struct clock_info { + int num_clocks; + int max_async_req; + struct scmi_clock_info *clk; +}; + +static struct clock_info clocks; + +static int scmi_clock_protocol_attributes_get(const struct scmi_handle *handle, + struct 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 = 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_one_xfer_put(handle, t); + return ret; +} + +static int scmi_clock_attributes_get(const struct scmi_handle *handle, + u32 clk_id, struct scmi_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 = t->rx.buf; + + ret = scmi_do_xfer(handle, t); + if (!ret) + memcpy(clk->name, attr->name, SCMI_MAX_STR_SIZE); + else + clk->name[0] = '\0'; + + scmi_one_xfer_put(handle, t); + return ret; +} + +static int +scmi_clock_describe_rates_get(const struct scmi_handle *handle, u32 clk_id, + struct scmi_clock_info *clk) +{ + u64 *rate; + int ret, cnt; + bool rate_discrete; + u32 tot_rate_cnt = 0, rates_flag; + 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 = t->tx.buf; + rlist = 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; + + rates_flag = le32_to_cpu(rlist->num_rates_flags); + num_remaining = NUM_REMAINING(rates_flag); + rate_discrete = RATE_DISCRETE(rates_flag); + num_returned = NUM_RETURNED(rates_flag); + + if (tot_rate_cnt + num_returned > SCMI_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->list.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); + + if (rate_discrete) + clk->list.num_rates = tot_rate_cnt; + + scmi_one_xfer_put(handle, t); + return ret; +} + +static int +scmi_clock_rate_get(const 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 = t->rx.buf; + + *value = le32_to_cpu(*pval); + *value |= (u64)le32_to_cpu(*(pval + 1)) << 32; + } + + scmi_one_xfer_put(handle, t); + return ret; +} + +static int scmi_clock_rate_set(const 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 = 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_one_xfer_put(handle, t); + return ret; +} + +static int +scmi_clock_config_set(const 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 = t->tx.buf; + cfg->id = cpu_to_le32(clk_id); + cfg->attributes = cpu_to_le32(config); + + ret = scmi_do_xfer(handle, t); + + scmi_one_xfer_put(handle, t); + return ret; +} + +static int scmi_clock_enable(const struct scmi_handle *handle, u32 clk_id) +{ + return scmi_clock_config_set(handle, clk_id, CLOCK_ENABLE); +} + +static int scmi_clock_disable(const struct scmi_handle *handle, u32 clk_id) +{ + return scmi_clock_config_set(handle, clk_id, 0); +} + +static int scmi_clock_count_get(const struct scmi_handle *handle) +{ + return clocks.num_clocks; +} + +static const struct scmi_clock_info * +scmi_clock_info_get(const struct scmi_handle *handle, u32 clk_id) +{ + struct scmi_clock_info *clk = clocks.clk + clk_id; + + if (!clk->name || !clk->name[0]) + return NULL; + + return clk; +} + +static struct scmi_clk_ops clk_ops = { + .count_get = scmi_clock_count_get, + .info_get = scmi_clock_info_get, + .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 clkid, ret; + u32 version; + + 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 scmi_clock_info), GFP_KERNEL); + if (!clocks.clk) + return -ENOMEM; + + for (clkid = 0; clkid < clocks.num_clocks; clkid++) { + struct scmi_clock_info *clk = clocks.clk + clkid; + + ret = scmi_clock_attributes_get(handle, clkid, clk); + if (!ret) + scmi_clock_describe_rates_get(handle, clkid, clk); + } + + handle->clk_ops = &clk_ops; + + return 0; +} diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h index c9f97e69444a..c15f37c86025 100644 --- a/include/linux/scmi_protocol.h +++ b/include/linux/scmi_protocol.h @@ -18,6 +18,7 @@ #include #define SCMI_MAX_STR_SIZE 16 +#define SCMI_MAX_NUM_RATES 16 /** * struct scmi_revision_info - version information structure @@ -43,9 +44,46 @@ struct scmi_revision_info { char sub_vendor_id[SCMI_MAX_STR_SIZE]; }; +struct scmi_clock_info { + char name[SCMI_MAX_STR_SIZE]; + bool rate_discrete; + union { + struct { + int num_rates; + u64 rates[SCMI_MAX_NUM_RATES]; + } list; + struct { + u64 min_rate; + u64 max_rate; + u64 step_size; + } range; + }; +}; + struct scmi_handle; /** + * struct scmi_clk_ops - represents the various operations provided + * by SCMI Clock Protocol + * + * @count_get: get the count of clocks provided by SCMI + * @info_get: get the information of the specified clock + * @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 (*count_get)(const struct scmi_handle *); + const struct scmi_clock_info *(*info_get)(const struct scmi_handle *, + u32); + int (*rate_get)(const struct scmi_handle *, u32, u64*); + int (*rate_set)(const struct scmi_handle *, u32, u32, u64); + int (*enable)(const struct scmi_handle *, u32); + int (*disable)(const struct scmi_handle *, u32); +}; + +/** * struct scmi_perf_ops - represents the various operations provided * by SCMI Performance Protocol * @@ -78,11 +116,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; }; #if IS_REACHABLE(CONFIG_ARM_SCMI_PROTOCOL) From patchwork Mon Jun 26 15:55:12 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sudeep Holla X-Patchwork-Id: 106363 Delivered-To: patch@linaro.org Received: by 10.140.101.48 with SMTP id t45csp168245qge; Mon, 26 Jun 2017 08:58:00 -0700 (PDT) X-Received: by 10.84.143.36 with SMTP id 33mr872737ply.171.1498492680008; Mon, 26 Jun 2017 08:58:00 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1498492680; cv=none; d=google.com; s=arc-20160816; b=nUHW+OY+xUXxBWwaBCg1XzHQfP5sH27l2b8rAnRRmbqGtAnYKyjCGW1xTHfpmyGc33 w4OBURxv23wU8aBArAnXdOFem2w/wcwIPcAfUekc/plxHGI9aq4rBytRh+Dz6Mb1+BR7 WdS4fbpXNOWoV26ZdpyqkZKz0+3nHbphULq07v689vlBgVLTPxON7Og8FLec8jm0DsBl ggUZLT1HYUdCxzqVv4RSzWTcwkKlO0m008JiY7N27o7Tcyx69osZTMlXvmPQzNFW7nNp XF9BvWzAVjixC5zQNViGkbV0D2E5MWwlVZtnklhLxb/w/dkvChuUA9mEu0wHwfGyy01k T7Yw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:arc-authentication-results; bh=qM9GaoO8cx8P/xsQ1+xNvcmhmxF0nhJgGzKZzUOH6Ww=; b=pW9H/hOKy5ZnwLeJEzLoCFWDUcXDQs6yzQ5HpjChytlCf5Mh9mgXdeHl4NGibIkZES OPgzjsJTDspxJEaX4WYzSfFsp59euWfMJrA4+Ixb+kVCG72YU23C3ykituUV2rfrgg7e ksK2+BSIkeDF4wrI/koJPyIHpOVbDhG93e49GmVWAgpoL5Whb+RRE9VYxN1jD6t0A35t oZMmzqbwEALYCWMGGgAEJV18VkCMDYAY8JP3bZGgOL/Ctr1JjKhaBYWNq7IHpxUw/Axf HME+AwxX4bl018oSApsFOFg5KJbXFSV8iXnVnv84mB3JsFZQ5vFS1qMKnK7R80r+ryiX JNfA== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of devicetree-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=devicetree-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id d89si265342pfl.148.2017.06.26.08.57.59; Mon, 26 Jun 2017 08:57:59 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of devicetree-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of devicetree-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=devicetree-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751895AbdFZP56 (ORCPT + 7 others); Mon, 26 Jun 2017 11:57:58 -0400 Received: from foss.arm.com ([217.140.101.70]:46122 "EHLO foss.arm.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752208AbdFZPzi (ORCPT ); Mon, 26 Jun 2017 11:55:38 -0400 Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.72.51.249]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 6EE3F16A3; Mon, 26 Jun 2017 08:55:37 -0700 (PDT) Received: from e107155-lin.cambridge.arm.com (e107155-lin.cambridge.arm.com [10.1.211.34]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPA id B364A3F4FF; Mon, 26 Jun 2017 08:55:35 -0700 (PDT) From: Sudeep Holla To: ALKML , LKML , DTML Cc: Sudeep Holla , Roy Franz , Harb Abdulhamid , Nishanth Menon , Arnd Bergmann , Loc Ho , Alexey Klimov , Matt Sealey Subject: [PATCH 8/9] firmware: arm_scmi: add initial support for sensor protocol Date: Mon, 26 Jun 2017 16:55:12 +0100 Message-Id: <1498492513-29771-9-git-send-email-sudeep.holla@arm.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1498492513-29771-1-git-send-email-sudeep.holla@arm.com> References: <1498492513-29771-1-git-send-email-sudeep.holla@arm.com> Sender: devicetree-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: devicetree@vger.kernel.org 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 --- drivers/firmware/arm_scmi/Makefile | 2 +- drivers/firmware/arm_scmi/sensors.c | 286 ++++++++++++++++++++++++++++++++++++ include/linux/scmi_protocol.h | 40 +++++ 3 files changed, 327 insertions(+), 1 deletion(-) create mode 100644 drivers/firmware/arm_scmi/sensors.c -- 2.7.4 -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html 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..9f1e254e7188 --- /dev/null +++ b/drivers/firmware/arm_scmi/sensors.c @@ -0,0 +1,286 @@ +/* + * 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 . + */ + +#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; +}; + +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]; +}; + +struct scmi_msg_set_sensor_config { + __le32 id; + __le32 event_control; +}; + +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; +}; + +struct scmi_msg_sensor_reading_get { + __le32 id; + __le32 flags; +#define SENSOR_READ_ASYNC BIT(0) +}; + +struct sensors_info { + int num_sensors; + int max_requests; + u64 reg_addr; + u32 reg_size; + struct scmi_sensor_info *sensors; +}; + +static struct sensors_info sensor_info; + +static int scmi_sensor_attributes_get(const struct scmi_handle *handle, + struct 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 = 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_one_xfer_put(handle, t); + return ret; +} + +static int scmi_sensor_description_get(const 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 *buf; + + ret = scmi_one_xfer_init(handle, SENSOR_DESCRIPTION_GET, + SCMI_PROTOCOL_SENSOR, sizeof(__le32), 0, &t); + if (ret) + return ret; + + buf = 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(buf->num_returned); + num_remaining = le16_to_cpu(buf->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++) { + u32 attrh; + struct scmi_sensor_info *s; + + attrh = le32_to_cpu(buf->desc[cnt].attributes_high); + s = &sensor_info.sensors[desc_index + cnt]; + s->id = le32_to_cpu(buf->desc[cnt].id); + s->type = SENSOR_TYPE(attrh); + memcpy(s->name, buf->desc[cnt].name, SCMI_MAX_STR_SIZE); + } + + desc_index += num_returned; + /* + * check for both returned and remaining to avoid infinite + * loop due to buggy firmware + */ + } while (num_returned && num_remaining); + + scmi_one_xfer_put(handle, t); + return ret; +} + +static int +scmi_sensor_configuration_set(const 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 = 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_one_xfer_put(handle, t); + return ret; +} + +static int scmi_sensor_trip_point_set(const 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 = 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_one_xfer_put(handle, t); + return ret; +} + +static int scmi_sensor_reading_get(const 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 = 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 = t->rx.buf; + + *value = le32_to_cpu(*pval); + *value |= (u64)le32_to_cpu(*(pval + 1)) << 32; + } + + scmi_one_xfer_put(handle, t); + return ret; +} + +static const struct scmi_sensor_info * +scmi_sensor_info_get(const struct scmi_handle *handle, u32 sensor_id) +{ + return sensor_info.sensors + sensor_id; +} + +static int scmi_sensor_count_get(const struct scmi_handle *handle) +{ + return sensor_info.num_sensors; +} + +static struct scmi_sensor_ops sensor_ops = { + .count_get = scmi_sensor_count_get, + .info_get = scmi_sensor_info_get, + .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; + + 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); + + sensor_info.sensors = devm_kcalloc(handle->dev, sensor_info.num_sensors, + sizeof(struct scmi_sensor_info), GFP_KERNEL); + if (!sensor_info.sensors) + return -ENOMEM; + + 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 e5a80511f88c..d2a34cc9df8b 100644 --- a/include/linux/scmi_protocol.h +++ b/include/linux/scmi_protocol.h @@ -136,6 +136,44 @@ struct scmi_power_ops { int (*state_notify_enable)(const struct scmi_handle *, u32, bool); }; +struct scmi_sensor_info { + u32 id; + u8 type; + char name[SCMI_MAX_STR_SIZE]; +}; + +/* + * Partial list from Distributed Management Task Force (DMTF) specification: + * DSP0249 (Platform Level Data Model specification) + */ +enum scmi_sensor_class { + TEMPERATURE_C = 0x2, + VOLTAGE = 0x5, + CURRENT = 0x6, + POWER = 0x7, + ENERGY = 0x8, +}; + +/** + * struct scmi_sensor_ops - represents the various operations provided + * by SCMI Sensor Protocol + * + * @count_get: get the count of sensors provided by SCMI + * @info_get: get the information of the specified sensor + * @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 (*count_get)(const struct scmi_handle *); + const struct scmi_sensor_info *(*info_get)(const struct scmi_handle *, + u32); + int (*configuration_set)(const struct scmi_handle *, u32); + int (*trip_point_set)(const struct scmi_handle *, u32, u8, u64); + int (*reading_get)(const struct scmi_handle *, u32, bool, u64 *); +}; + /** * struct scmi_handle - Handle returned to ARM SCMI clients for usage. * @@ -144,6 +182,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; @@ -151,6 +190,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; }; #if IS_REACHABLE(CONFIG_ARM_SCMI_PROTOCOL)