[3/9] firmware: arm_scmi: add basic driver infrastructure for SCMI

Message ID 1498492513-29771-4-git-send-email-sudeep.holla@arm.com
State New
Headers show
Series
  • firmware: ARM System Control and Management Interface(SCMI) support
Related show

Commit Message

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

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

Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>

---
 drivers/firmware/Kconfig           |  21 ++
 drivers/firmware/Makefile          |   1 +
 drivers/firmware/arm_scmi/Makefile |   2 +
 drivers/firmware/arm_scmi/common.h |  74 ++++
 drivers/firmware/arm_scmi/driver.c | 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

Comments

Sudeep Holla July 7, 2017, 5:39 p.m. | #1
On 07/07/17 17:52, Jassi Brar wrote:
> Hi Arnd, Hi Rob, Hi Mark,

> 

> [CC'ing only those who I have the email id of]

> 

>> +/**

>> + * 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);

>> +

>>

> The api is

> 

>     int mbox_send_message(struct mbox_chan *chan, void *mssg)

> 

> where each controller driver defines its own format in which it accepts

> the 'mssg' to be transmitted.

> 


Yes they can continue that, but SCMI just doesn't depend on that.

> For example :-

> ti_msgmgr_send_data(struct mbox_chan *, struct ti_msgmgr_message *)

> rockchip_mbox_send_data(struct mbox_chan *, struct rockchip_mbox_msg *)

> ....and so on...  you get the idea.

> 


Yes I am aware of that.

> Some controller driver may ignore the 'mssg' because only an interrupt line

> is shared with the remote and not some register/fifo.

> For example,

>   sti_mbox_send_data(struct mbox_chan *, void *ignored)

> 


Exactly, now with SCMI, every controller *can do* that, as we just care
about the signaling which in other terms I have so far referred as
"doorbell". The data passed should not be used for any other purpose
as there's no need to even by the remote firmware implementing SCMI.

If you read the SCMI specification, it's designed to avoid all such
issues with wide variety of mailbox controller we have in the wild.
And hence all the command and other information are passed in the shared
memory.

> Out of 14 controller drivers today, this SCMI implementation will work with

> only 5 (and that too by abusing the api).


What do you mean by abusing the API ?

> The other 9 controllers (and many

> in future) are perfectly able to support SCMI (its just another protocol).


Exactly, if and only if they adhere to the specification.

> All we need is what the SCMI specification calls "Transport Layer".

> Of course that will be a thin platform/controller specific code that will

> encapsulate the 'struct scmi_xfer *xfer' from SCMI, into controller specified

> format and actually call the mbox_send_message().


OK, if platform had to send some platform specific data, I agree. But
can you point me from the SCPI specification the need for the platforms
to do that. That's the whole point. Transport is just a doorbell nothing
more.

You are trying to fit the existing platform implementation into SCMI
which won't work.

E.g. if TI/Rockchip implements SCMI, they can continue using these
drivers as along as it does the job of "ringing the doorbell".

They may be passing some data today because their existing remote
firmware expects it. With SCMI, they don't have to. These existing
drivers are doing additional job in the current form: passing some data
along with triggering the remote doorbell. We just need latter in SCMI
case as we don't need any data in the transport as it's all part of shmem.

Why do you think that won't work ? Remember all the platform specific
stuff(if platform implement own protocols) in SCMI is moved to the
shared memory and transport just needs to do "doorbell". So I disagree
with your concern unless I am missing something.

> 

>  To achieve that, we can either do similar to what platforms with DWC3

> do - generic dwc3 child node of platform specific parent node. Or we can have

> the SCMI protocol implemented as a library that platforms can init and use.

> Or any other better way.

> 

>  I just think it is a bad idea to rule out all but one classes of mailbox controllers.

> 


Definitely neither SCMI specification nor SCMI driver is ruling out any
class of mailbox controllers as long as there are capable of signaling
the other end that "hey look you have something to check in shared
memory" which I think is the case with most of the controllers.

To be honest this whole mailbox framework is somewhat complex for simple
doorbell kind of this, but hey I am not complaining ;).

-- 
Regards,
Sudeep
--
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
Jassi Brar July 8, 2017, 5:32 a.m. | #2
Hi Roy, Matt, Nishant, Harb Abdulhamid, Loc,

I have a gut feeling you guys were part of the SCMI spec committee. If
so, could you please chime in?


On Fri, Jul 7, 2017 at 11:09 PM, Sudeep Holla <sudeep.holla@arm.com> wrote:
>

>

> On 07/07/17 17:52, Jassi Brar wrote:

>> Hi Arnd, Hi Rob, Hi Mark,

>>

>> [CC'ing only those who I have the email id of]

>>

>>> +/**

>>> + * 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);

>>> +

>>>

>> The api is

>>

>>     int mbox_send_message(struct mbox_chan *chan, void *mssg)

>>

>> where each controller driver defines its own format in which it accepts

>> the 'mssg' to be transmitted.

>>

>

> Yes they can continue that, but SCMI just doesn't depend on that.

>

>> For example :-

>> ti_msgmgr_send_data(struct mbox_chan *, struct ti_msgmgr_message *)

>> rockchip_mbox_send_data(struct mbox_chan *, struct rockchip_mbox_msg *)

>> ....and so on...  you get the idea.

>>

>

> Yes I am aware of that.

>

>> Some controller driver may ignore the 'mssg' because only an interrupt line

>> is shared with the remote and not some register/fifo.

>> For example,

>>   sti_mbox_send_data(struct mbox_chan *, void *ignored)

>>

>

> Exactly, now with SCMI, every controller *can do* that, as we just care

> about the signaling which in other terms I have so far referred as

> "doorbell".

>

No, the controllers can not ... unless you clone and adapt the 9
drivers+bindings to conform to the expectations of SCMI (like you
attempted with MHU recently). Also, then mandate every future
controller driver must emulate "doorbell" channels.

As the mailbox maintainer, I am open to suggestions that would allow
every controller to support SCMI.
But compared to the options of scmi-as-a-library and
scmi-as-child-node-of-platform-parent, this does not even qualify as
an option.

Why? Because SCMI is but one protocol that provides 4 features ATM,
and certainly can not provide for every whim and quirk of future
platforms.  Among the sane requirements are watchdog,
suspend/resume/hibernation and thermal _control_ (not just sensor
readings) and among the weird are video, network and storage over
mailbox api. And even a filesystem backed by read/write over mailbox!!
 And these are only that I have worked on first hand.

  The point is : you can not assume SCMI to be the only protocol
running over a controller _and_ you can not dictate other protocols to
not touch certain bits of the signal register/fifo.

In simplest terms, controller driver can not cater to only a
particular client. That's the reason we have the controller driver
define the message format and clients conform to it.

Thanks.
--
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
Sudeep Holla July 24, 2017, 9:50 a.m. | #3
On 08/07/17 06:32, Jassi Brar wrote:
> Hi Roy, Matt, Nishant, Harb Abdulhamid, Loc,

> 

> I have a gut feeling you guys were part of the SCMI spec committee. If

> so, could you please chime in?

> 


I take complete silence as no objection.

> 

> On Fri, Jul 7, 2017 at 11:09 PM, Sudeep Holla <sudeep.holla@arm.com> wrote:

>>

>>

>> On 07/07/17 17:52, Jassi Brar wrote:

>>> Hi Arnd, Hi Rob, Hi Mark,

>>>

>>> [CC'ing only those who I have the email id of]

>>>

>>>> +/**

>>>> + * 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);

>>>> +

>>>>

>>> The api is

>>>

>>>     int mbox_send_message(struct mbox_chan *chan, void *mssg)

>>>

>>> where each controller driver defines its own format in which it accepts

>>> the 'mssg' to be transmitted.

>>>

>>

>> Yes they can continue that, but SCMI just doesn't depend on that.

>>

>>> For example :-

>>> ti_msgmgr_send_data(struct mbox_chan *, struct ti_msgmgr_message *)

>>> rockchip_mbox_send_data(struct mbox_chan *, struct rockchip_mbox_msg *)

>>> ....and so on...  you get the idea.

>>>

>>

>> Yes I am aware of that.

>>

>>> Some controller driver may ignore the 'mssg' because only an interrupt line

>>> is shared with the remote and not some register/fifo.

>>> For example,

>>>   sti_mbox_send_data(struct mbox_chan *, void *ignored)

>>>

>>

>> Exactly, now with SCMI, every controller *can do* that, as we just care

>> about the signaling which in other terms I have so far referred as

>> "doorbell".

>>


You keep combining the 2 changes. Please treat them differently.
I need ARM MHU doorbell changes as I need to support multiple channels
on my platform which are independent and run different protocol.
That doorbell change *is not SCMI* specific. I am not sure how to conver
that message to you. All my attempts have failed so far.

> No, the controllers can not ... unless you clone and adapt the 9

> drivers+bindings to conform to the expectations of SCMI (like you

> attempted with MHU recently). Also, then mandate every future

> controller driver must emulate "doorbell" channels.

> 


Why do you think so ? Please give me your understanding on what SCMI
specification expects as you seem to think we need to clone ?
All I am saying is we don't pass any information in the controller.
The current drivers just need a way to trigger the remote and I believe
all of them already have.

> As the mailbox maintainer, I am open to suggestions that would allow

> every controller to support SCMI.


That's good to know.

> But compared to the options of scmi-as-a-library and

> scmi-as-child-node-of-platform-parent, this does not even qualify as

> an option.

> 


I don't understand this.

> Why? Because SCMI is but one protocol that provides 4 features ATM,

> and certainly can not provide for every whim and quirk of future

> platforms.  Among the sane requirements are watchdog,

> suspend/resume/hibernation and thermal _control_ (not just sensor

> readings) and among the weird are video, network and storage over

> mailbox api. And even a filesystem backed by read/write over mailbox!!

>  And these are only that I have worked on first hand.

> 


Fine. We need to support them I am not saying that we don't. Please
answer my question above which I believe should reduce the confusion.

>   The point is : you can not assume SCMI to be the only protocol

> running over a controller _and_ you can not dictate other protocols to

> not touch certain bits of the signal register/fifo.

> 


NO, I am saying again that I am not assuming that and you are the one
who is assuming MHU *must* work in the way the driver is written
currently and constantly/repeatedly ignoring the fact what MHY
specification states and mixing that change with SCMI here.

> In simplest terms, controller driver can not cater to only a

> particular client. That's the reason we have the controller driver

> define the message format and clients conform to it.


100%. You need to understand the fact that my changes makes it work with
SCMI and other protocol and MHU as it stands only work with whatever
platform you have with whatever client that supports.

-- 
Regards,
Sudeep
--
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
Jassi Brar July 24, 2017, 3:41 p.m. | #4
On Mon, Jul 24, 2017 at 3:20 PM, Sudeep Holla <sudeep.holla@arm.com> wrote:
>

>

> On 08/07/17 06:32, Jassi Brar wrote:

>> Hi Roy, Matt, Nishant, Harb Abdulhamid, Loc,

>>

>> I have a gut feeling you guys were part of the SCMI spec committee. If

>> so, could you please chime in?

>>

>

> I take complete silence as no objection.

>

I take it as no self respecting developer/architect would stand by
your FUBAR implementation of SCMI.

SCMI  calls
    mbox_send_message(struct mbox_chan *chan, struct scmi_xfer *xfer);

whereas the API expects
    mbox_send_message(struct mbox_chan *chan, struct controller_specific *xfer);

As the maintainer I have tried to explain it to you in a dozen ways.
If you still can't see how it's broken, you can't be helped.

Checking out until someone else has any point.
NAK.
--
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
Sudeep Holla July 24, 2017, 4:21 p.m. | #5
On 24/07/17 16:41, Jassi Brar wrote:
> On Mon, Jul 24, 2017 at 3:20 PM, Sudeep Holla <sudeep.holla@arm.com> wrote:

>>

>>

>> On 08/07/17 06:32, Jassi Brar wrote:

>>> Hi Roy, Matt, Nishant, Harb Abdulhamid, Loc,

>>>

>>> I have a gut feeling you guys were part of the SCMI spec committee. If

>>> so, could you please chime in?

>>>

>>

>> I take complete silence as no objection.

>>

> I take it as no self respecting developer/architect would stand by

> your FUBAR implementation of SCMI.

>


Wow really ? This is exactly how ACPI PCC works today IIUC.

> SCMI  calls

>     mbox_send_message(struct mbox_chan *chan, struct scmi_xfer *xfer);

> 

> whereas the API expects

>     mbox_send_message(struct mbox_chan *chan, struct controller_specific *xfer);

> 


That's not hard to change but you seem to have combined my ARM MHU
changes into this which is totally wrong and now I say also stupid after
mentioning the same thing so many times. Consider them independently and
try to understand.

> As the maintainer I have tried to explain it to you in a dozen ways.

> If you still can't see how it's broken, you can't be helped.

> 


Sorry but you wanted driver to explain initially. I posted them. But you
haven't answer my specific questions yet.

-- 
Regards,
Sudeep
--
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

Patch hide | download patch | download mbox

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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/completion.h>
+#include <linux/scmi_protocol.h>
+#include <linux/types.h>
+
+/**
+ * struct scmi_msg_hdr - Message(Tx/Rx) header
+ *
+ * @id: The identifier of the command being sent
+ * @protocol_id: The identifier of the protocol used to send @id command
+ * @seq: The token to identify the message. when a message/command returns,
+ *       the platform returns the whole message header unmodified including
+ *	 the token.
+ */
+struct scmi_msg_hdr {
+	u8 id;
+	u8 protocol_id;
+	u16 seq;
+	u32 status;
+	bool poll_completion;
+};
+
+/**
+ * struct scmi_msg - Message(Tx/Rx) structure
+ *
+ * @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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/bitmap.h>
+#include <linux/export.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/mailbox_client.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/semaphore.h>
+#include <linux/slab.h>
+
+#include "common.h"
+
+#define MSG_ID_SHIFT		0
+#define MSG_ID_MASK		0xff
+#define MSG_TYPE_SHIFT		8
+#define MSG_TYPE_MASK		0x3
+#define MSG_PROTOCOL_ID_SHIFT	10
+#define MSG_PROTOCOL_ID_MASK	0xff
+#define MSG_TOKEN_ID_SHIFT	18
+#define MSG_TOKEN_ID_MASK	0x3ff
+#define MSG_XTRACT_TOKEN(header)	\
+	(((header) >> MSG_TOKEN_ID_SHIFT) & MSG_TOKEN_ID_MASK)
+
+enum scmi_error_codes {
+	SCMI_SUCCESS = 0,	/* Success */
+	SCMI_ERR_SUPPORT = -1,	/* Not supported */
+	SCMI_ERR_PARAMS = -2,	/* Invalid Parameters */
+	SCMI_ERR_ACCESS = -3,	/* Invalid access/permission denied */
+	SCMI_ERR_ENTRY = -4,	/* Not found */
+	SCMI_ERR_RANGE = -5,	/* Value out of range */
+	SCMI_ERR_BUSY = -6,	/* Device busy */
+	SCMI_ERR_COMMS = -7,	/* Communication Error */
+	SCMI_ERR_GENERIC = -8,	/* Generic Error */
+	SCMI_ERR_HARDWARE = -9,	/* Hardware Error */
+	SCMI_ERR_PROTOCOL = -10,/* Protocol Error */
+	SCMI_ERR_MAX
+};
+
+/* List of all  SCMI devices active in system */
+static LIST_HEAD(scmi_list);
+/* Protection for the entire list */
+static DEFINE_MUTEX(scmi_list_mutex);
+
+/**
+ * struct scmi_xfers_info - Structure to manage transfer information
+ *
+ * @sem_xfer_count: Counting Semaphore for managing max simultaneous
+ *	Messages.
+ * @xfer_block: Preallocated Message array
+ * @xfer_alloc_table: Bitmap table for allocated messages.
+ *	Index of this bitmap table is also used for message
+ *	sequence identifier.
+ * @xfer_lock: Protection for message allocation
+ */
+struct scmi_xfers_info {
+	struct semaphore sem_xfer_count;
+	struct scmi_xfer *xfer_block;
+	unsigned long *xfer_alloc_table;
+	/* protect transfer allocation */
+	spinlock_t xfer_lock;
+};
+
+/**
+ * struct scmi_desc - Description of SoC integration
+ *
+ * @max_rx_timeout_ms: Timeout for communication with SoC (in Milliseconds)
+ * @max_msg: Maximum number of messages that can be pending
+ *	simultaneously in the system
+ * @max_msg_size: Maximum size of data per message that can be handled.
+ */
+struct scmi_desc {
+	int max_rx_timeout_ms;
+	int max_msg;
+	int max_msg_size;
+};
+
+/**
+ * struct scmi_info - Structure representing a  SCMI instance
+ *
+ * @dev: Device pointer
+ * @desc: SoC description for this instance
+ * @handle: Instance of SCMI handle to send to clients
+ * @cl: Mailbox Client
+ * @tx_chan: Transmit mailbox channel
+ * @rx_chan: Receive mailbox channel
+ * @tx_payload: Transmit mailbox channel payload area
+ * @rx_payload: Receive mailbox channel payload area
+ * @minfo: Message info
+ * @node: list head
+ * @users: Number of users of this instance
+ */
+struct scmi_info {
+	struct device *dev;
+	const struct scmi_desc *desc;
+	struct scmi_handle handle;
+	struct mbox_client cl;
+	struct mbox_chan *tx_chan;
+	struct mbox_chan *rx_chan;
+	void __iomem *tx_payload;
+	void __iomem *rx_payload;
+	struct scmi_xfers_info minfo;
+	struct list_head node;
+	int users;
+};
+
+#define client_to_scmi_info(c)	container_of(c, struct scmi_info, cl)
+#define handle_to_scmi_info(h)	container_of(h, struct scmi_info, handle)
+
+/*
+ * The SCP firmware 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 <sudeep.holla@arm.com>");
+MODULE_DESCRIPTION("ARM SCMI protocol driver");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h
new file mode 100644
index 000000000000..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 <http://www.gnu.org/licenses/>.
+ */
+#include <linux/types.h>
+
+/**
+ * struct scmi_handle - Handle returned to ARM SCMI clients for usage.
+ *
+ * @dev: pointer to the SCMI device
+ */
+struct scmi_handle {
+	struct device *dev;
+};
+
+#if IS_REACHABLE(CONFIG_ARM_SCMI_PROTOCOL)
+int scmi_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 */