1139825Simp// SPDX-License-Identifier: GPL-2.0+
299663Sbenno/*
399663Sbenno * Copyright (C) 2020 Linaro Limited.
499663Sbenno */
599663Sbenno
699663Sbenno#define LOG_CATEGORY UCLASS_SCMI_AGENT
799663Sbenno
899663Sbenno#include <common.h>
999663Sbenno#include <dm.h>
1099663Sbenno#include <errno.h>
1199663Sbenno#include <mailbox.h>
1299663Sbenno#include <scmi_agent.h>
1399663Sbenno#include <scmi_agent-uclass.h>
1499663Sbenno#include <dm/device_compat.h>
1599663Sbenno#include <dm/devres.h>
1699663Sbenno#include <linux/compat.h>
1799663Sbenno
1899663Sbenno#include "smt.h"
1999663Sbenno
2099663Sbenno#define TIMEOUT_US_10MS			10000
2199663Sbenno
2299663Sbenno/**
2399663Sbenno * struct scmi_mbox_channel - Description of an SCMI mailbox transport
2499663Sbenno * @smt:	Shared memory buffer
2599663Sbenno * @mbox:	Mailbox channel description
2699663Sbenno * @timeout_us:	Timeout in microseconds for the mailbox transfer
2799663Sbenno */
2899663Sbennostruct scmi_mbox_channel {
2999663Sbenno	struct scmi_smt smt;
30131102Sgrehan	struct mbox_chan mbox;
3199663Sbenno	ulong timeout_us;
3299663Sbenno};
3399663Sbenno
3499663Sbenno/**
3599663Sbenno * struct scmi_channel - Channel instance referenced in SCMI drivers
3699663Sbenno * @ref: Reference to local channel instance
37183882Snwhitehorn **/
38186128Snwhitehornstruct scmi_channel {
3999663Sbenno	struct scmi_mbox_channel ref;
4099663Sbenno};
4199663Sbenno
4299663Sbennostatic int scmi_mbox_process_msg(struct udevice *dev,
4399663Sbenno				 struct scmi_channel *channel,
44208149Snwhitehorn				 struct scmi_msg *msg)
4599663Sbenno{
46174782Smarcel	struct scmi_mbox_channel *chan = &channel->ref;
4799663Sbenno	int ret;
4899663Sbenno
4999663Sbenno	ret = scmi_write_msg_to_smt(dev, &chan->smt, msg);
5099663Sbenno	if (ret)
5199663Sbenno		return ret;
5299663Sbenno
5399663Sbenno	/* Give shm addr to mbox in case it is meaningful */
5499663Sbenno	ret = mbox_send(&chan->mbox, chan->smt.buf);
5599663Sbenno	if (ret) {
56208149Snwhitehorn		dev_err(dev, "Message send failed: %d\n", ret);
57208149Snwhitehorn		goto out;
58208149Snwhitehorn	}
5999663Sbenno
60208149Snwhitehorn	/* Receive the response */
6199663Sbenno	ret = mbox_recv(&chan->mbox, chan->smt.buf, chan->timeout_us);
6299663Sbenno	if (ret) {
6399663Sbenno		dev_err(dev, "Response failed: %d, abort\n", ret);
6499663Sbenno		goto out;
6599663Sbenno	}
66208149Snwhitehorn
67208149Snwhitehorn	ret = scmi_read_resp_from_smt(dev, &chan->smt, msg);
68208149Snwhitehorn
6999663Sbennoout:
7099663Sbenno	scmi_clear_smt_channel(&chan->smt);
7199663Sbenno
72208149Snwhitehorn	return ret;
73208149Snwhitehorn}
74208149Snwhitehorn
75208149Snwhitehornstatic int setup_channel(struct udevice *dev, struct scmi_mbox_channel *chan)
76208149Snwhitehorn{
77208149Snwhitehorn	int ret;
78208149Snwhitehorn
79208149Snwhitehorn	ret = mbox_get_by_index(dev, 0, &chan->mbox);
80208149Snwhitehorn	if (ret) {
81208149Snwhitehorn		dev_err(dev, "Failed to find mailbox: %d\n", ret);
82208149Snwhitehorn		return ret;
8399663Sbenno	}
8499663Sbenno
85183882Snwhitehorn	ret = scmi_dt_get_smt_buffer(dev, &chan->smt);
86183882Snwhitehorn	if (ret) {
87183882Snwhitehorn		dev_err(dev, "Failed to get shm resources: %d\n", ret);
88208149Snwhitehorn		return ret;
89183882Snwhitehorn	}
90183882Snwhitehorn
91208149Snwhitehorn	chan->timeout_us = TIMEOUT_US_10MS;
9299663Sbenno
9399663Sbenno	return 0;
94208149Snwhitehorn}
95208149Snwhitehorn
96208149Snwhitehornstatic int scmi_mbox_get_channel(struct udevice *dev,
9799663Sbenno				 struct udevice *protocol,
9899663Sbenno				 struct scmi_channel **channel)
9999663Sbenno{
100208149Snwhitehorn	struct scmi_mbox_channel *base_chan = dev_get_plat(dev);
101208149Snwhitehorn	struct scmi_mbox_channel *chan;
10299663Sbenno	int ret;
103208149Snwhitehorn
104208149Snwhitehorn	if (!dev_read_prop(protocol, "shmem", NULL)) {
10599663Sbenno		/* Uses agent base channel */
10699663Sbenno		*channel = container_of(base_chan, struct scmi_channel, ref);
107208149Snwhitehorn
108208149Snwhitehorn		return 0;
109208149Snwhitehorn	}
110208149Snwhitehorn
11199663Sbenno	chan = calloc(1, sizeof(*chan));
112208149Snwhitehorn	if (!chan)
113208149Snwhitehorn		return -ENOMEM;
114208149Snwhitehorn
115208149Snwhitehorn	/* Setup a dedicated channel for the protocol */
116208149Snwhitehorn	ret = setup_channel(protocol, chan);
11799663Sbenno	if (ret) {
118208871Snwhitehorn		free(chan);
119208871Snwhitehorn		return ret;
120208149Snwhitehorn	}
121208149Snwhitehorn
122208149Snwhitehorn	*channel = (void *)chan;
123208149Snwhitehorn
124208149Snwhitehorn	return 0;
125208149Snwhitehorn}
126208149Snwhitehorn
127183882Snwhitehornint scmi_mbox_of_to_plat(struct udevice *dev)
12899663Sbenno{
12999663Sbenno	struct scmi_mbox_channel *chan = dev_get_plat(dev);
13099663Sbenno
131208149Snwhitehorn	return setup_channel(dev, chan);
132208149Snwhitehorn}
133208149Snwhitehorn
134208149Snwhitehornstatic const struct udevice_id scmi_mbox_ids[] = {
13599663Sbenno	{ .compatible = "arm,scmi" },
13699663Sbenno	{ }
137208149Snwhitehorn};
13899663Sbenno
139260674Sjhibbitsstatic const struct scmi_agent_ops scmi_mbox_ops = {
140260674Sjhibbits	.of_get_channel = scmi_mbox_get_channel,
141260674Sjhibbits	.process_msg = scmi_mbox_process_msg,
142260674Sjhibbits};
143260674Sjhibbits
144260674SjhibbitsU_BOOT_DRIVER(scmi_mbox) = {
145260674Sjhibbits	.name		= "scmi-over-mailbox",
146266160Sian	.id		= UCLASS_SCMI_AGENT,
14799663Sbenno	.of_match	= scmi_mbox_ids,
148208149Snwhitehorn	.plat_auto	= sizeof(struct scmi_mbox_channel),
149208149Snwhitehorn	.of_to_plat	= scmi_mbox_of_to_plat,
150208149Snwhitehorn	.ops		= &scmi_mbox_ops,
151208149Snwhitehorn};
152208149Snwhitehorn