xref: /freebsd/sys/dev/firmware/arm/scmi.c (revision a0ba2a97)
154b96380SRuslan Bukin /*-
254b96380SRuslan Bukin  * SPDX-License-Identifier: BSD-2-Clause
354b96380SRuslan Bukin  *
454b96380SRuslan Bukin  * Copyright (c) 2022 Ruslan Bukin <br@bsdpad.com>
5d220b1cfSCristian Marussi  * Copyright (c) 2023 Arm Ltd
654b96380SRuslan Bukin  *
754b96380SRuslan Bukin  * This work was supported by Innovate UK project 105694, "Digital Security
854b96380SRuslan Bukin  * by Design (DSbD) Technology Platform Prototype".
954b96380SRuslan Bukin  *
1054b96380SRuslan Bukin  * Redistribution and use in source and binary forms, with or without
1154b96380SRuslan Bukin  * modification, are permitted provided that the following conditions
1254b96380SRuslan Bukin  * are met:
1354b96380SRuslan Bukin  * 1. Redistributions of source code must retain the above copyright
1454b96380SRuslan Bukin  *    notice, this list of conditions and the following disclaimer.
1554b96380SRuslan Bukin  * 2. Redistributions in binary form must reproduce the above copyright
1654b96380SRuslan Bukin  *    notice, this list of conditions and the following disclaimer in the
1754b96380SRuslan Bukin  *    documentation and/or other materials provided with the distribution.
1854b96380SRuslan Bukin  *
1954b96380SRuslan Bukin  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
2054b96380SRuslan Bukin  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2154b96380SRuslan Bukin  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2254b96380SRuslan Bukin  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2354b96380SRuslan Bukin  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2454b96380SRuslan Bukin  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2554b96380SRuslan Bukin  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2654b96380SRuslan Bukin  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2754b96380SRuslan Bukin  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2854b96380SRuslan Bukin  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2954b96380SRuslan Bukin  * SUCH DAMAGE.
3054b96380SRuslan Bukin  */
3154b96380SRuslan Bukin 
3254b96380SRuslan Bukin #include <sys/param.h>
3354b96380SRuslan Bukin #include <sys/systm.h>
3454b96380SRuslan Bukin #include <sys/bus.h>
3554b96380SRuslan Bukin #include <sys/cpu.h>
3654b96380SRuslan Bukin #include <sys/kernel.h>
3754b96380SRuslan Bukin #include <sys/lock.h>
3854b96380SRuslan Bukin #include <sys/module.h>
3954b96380SRuslan Bukin #include <sys/mutex.h>
4054b96380SRuslan Bukin 
41be82b3a0SEmmanuel Vadot #include <dev/clk/clk.h>
4254b96380SRuslan Bukin #include <dev/fdt/simplebus.h>
4354b96380SRuslan Bukin #include <dev/fdt/fdt_common.h>
4454b96380SRuslan Bukin #include <dev/ofw/ofw_bus_subr.h>
4554b96380SRuslan Bukin 
4654b96380SRuslan Bukin #include "dev/mailbox/arm/arm_doorbell.h"
4754b96380SRuslan Bukin 
4854b96380SRuslan Bukin #include "scmi.h"
4954b96380SRuslan Bukin #include "scmi_protocols.h"
50d220b1cfSCristian Marussi #include "scmi_shmem.h"
5154b96380SRuslan Bukin 
52d220b1cfSCristian Marussi #define	SCMI_HDR_TOKEN_S		18
53d220b1cfSCristian Marussi #define SCMI_HDR_TOKEN_BF		(0x3fff)
54d220b1cfSCristian Marussi #define	SCMI_HDR_TOKEN_M		(SCMI_HDR_TOKEN_BF << SCMI_HDR_TOKEN_S)
5554b96380SRuslan Bukin 
56d220b1cfSCristian Marussi #define	SCMI_HDR_PROTOCOL_ID_S		10
57d220b1cfSCristian Marussi #define	SCMI_HDR_PROTOCOL_ID_BF		(0xff)
58d220b1cfSCristian Marussi #define	SCMI_HDR_PROTOCOL_ID_M		\
59d220b1cfSCristian Marussi     (SCMI_HDR_PROTOCOL_ID_BF << SCMI_HDR_PROTOCOL_ID_S)
6054b96380SRuslan Bukin 
61d220b1cfSCristian Marussi #define	SCMI_HDR_MESSAGE_TYPE_S		8
62d220b1cfSCristian Marussi #define	SCMI_HDR_MESSAGE_TYPE_BF	(0x3)
63d220b1cfSCristian Marussi #define	SCMI_HDR_MESSAGE_TYPE_M		\
64d220b1cfSCristian Marussi     (SCMI_HDR_MESSAGE_TYPE_BF << SCMI_HDR_MESSAGE_TYPE_S)
6554b96380SRuslan Bukin 
66d220b1cfSCristian Marussi #define	SCMI_HDR_MESSAGE_ID_S		0
67d220b1cfSCristian Marussi #define	SCMI_HDR_MESSAGE_ID_BF		(0xff)
68d220b1cfSCristian Marussi #define	SCMI_HDR_MESSAGE_ID_M		\
69d220b1cfSCristian Marussi     (SCMI_HDR_MESSAGE_ID_BF << SCMI_HDR_MESSAGE_ID_S)
7054b96380SRuslan Bukin 
71d220b1cfSCristian Marussi #define SCMI_MSG_TYPE_CMD	0
72d220b1cfSCristian Marussi #define SCMI_MSG_TYPE_DRESP	2
73d220b1cfSCristian Marussi #define SCMI_MSG_TYPE_NOTIF	3
7454b96380SRuslan Bukin 
7554b96380SRuslan Bukin static int
7654b96380SRuslan Bukin scmi_request_locked(struct scmi_softc *sc, struct scmi_req *req)
7754b96380SRuslan Bukin {
78d220b1cfSCristian Marussi 	uint32_t reply_header;
79d46f01fdSAndrew Turner 	int ret;
8054b96380SRuslan Bukin 
8154b96380SRuslan Bukin 	SCMI_ASSERT_LOCKED(sc);
8254b96380SRuslan Bukin 
83d220b1cfSCristian Marussi 	req->msg_header = req->message_id << SCMI_HDR_MESSAGE_ID_S;
84d46f01fdSAndrew Turner 	/* TODO: Allocate a token */
85d220b1cfSCristian Marussi 	req->msg_header |= SCMI_MSG_TYPE_CMD << SCMI_HDR_MESSAGE_TYPE_S;
86d220b1cfSCristian Marussi 	req->msg_header |= req->protocol_id << SCMI_HDR_PROTOCOL_ID_S;
8754b96380SRuslan Bukin 
88cbcfdff0SCristian Marussi 	ret = scmi_shmem_prepare_msg(sc->a2p_dev, req, cold);
89d220b1cfSCristian Marussi 	if (ret != 0)
90d220b1cfSCristian Marussi 		return (ret);
9154b96380SRuslan Bukin 
92d46f01fdSAndrew Turner 	ret = SCMI_XFER_MSG(sc->dev);
93d46f01fdSAndrew Turner 	if (ret != 0)
94a0ba2a97SCristian Marussi 		goto out;
9554b96380SRuslan Bukin 
9654b96380SRuslan Bukin 	/* Read header. */
97d220b1cfSCristian Marussi 	ret = scmi_shmem_read_msg_header(sc->a2p_dev, &reply_header);
98d220b1cfSCristian Marussi 	if (ret != 0)
99a0ba2a97SCristian Marussi 		goto out;
100a0ba2a97SCristian Marussi 
101a0ba2a97SCristian Marussi 	if (reply_header != req->msg_header) {
102a0ba2a97SCristian Marussi 		ret = EPROTO;
103a0ba2a97SCristian Marussi 		goto out;
104a0ba2a97SCristian Marussi 	}
105a0ba2a97SCristian Marussi 
106a0ba2a97SCristian Marussi 	ret = scmi_shmem_read_msg_payload(sc->a2p_dev, req->out_buf,
107a0ba2a97SCristian Marussi 	    req->out_size);
108a0ba2a97SCristian Marussi 
109a0ba2a97SCristian Marussi out:
110a0ba2a97SCristian Marussi 	scmi_shmem_tx_complete(sc->a2p_dev);
111a0ba2a97SCristian Marussi 
112d220b1cfSCristian Marussi 	return (ret);
11354b96380SRuslan Bukin }
11454b96380SRuslan Bukin 
11554b96380SRuslan Bukin int
11654b96380SRuslan Bukin scmi_request(device_t dev, struct scmi_req *req)
11754b96380SRuslan Bukin {
11854b96380SRuslan Bukin 	struct scmi_softc *sc;
11954b96380SRuslan Bukin 	int error;
12054b96380SRuslan Bukin 
12154b96380SRuslan Bukin 	sc = device_get_softc(dev);
12254b96380SRuslan Bukin 
12354b96380SRuslan Bukin 	SCMI_LOCK(sc);
12454b96380SRuslan Bukin 	error = scmi_request_locked(sc, req);
12554b96380SRuslan Bukin 	SCMI_UNLOCK(sc);
12654b96380SRuslan Bukin 
12754b96380SRuslan Bukin 	return (error);
12854b96380SRuslan Bukin }
12954b96380SRuslan Bukin 
130d46f01fdSAndrew Turner int
13154b96380SRuslan Bukin scmi_attach(device_t dev)
13254b96380SRuslan Bukin {
13354b96380SRuslan Bukin 	struct scmi_softc *sc;
13454b96380SRuslan Bukin 	phandle_t node;
13554b96380SRuslan Bukin 	int error;
13654b96380SRuslan Bukin 
13754b96380SRuslan Bukin 	sc = device_get_softc(dev);
13854b96380SRuslan Bukin 	sc->dev = dev;
13954b96380SRuslan Bukin 
14054b96380SRuslan Bukin 	node = ofw_bus_get_node(dev);
14154b96380SRuslan Bukin 	if (node == -1)
14254b96380SRuslan Bukin 		return (ENXIO);
14354b96380SRuslan Bukin 
144d220b1cfSCristian Marussi 	sc->a2p_dev = scmi_shmem_get(dev, node, SCMI_CHAN_A2P);
145d220b1cfSCristian Marussi 	if (sc->a2p_dev == NULL) {
146d220b1cfSCristian Marussi 		device_printf(dev, "A2P shmem dev not found.\n");
14754b96380SRuslan Bukin 		return (ENXIO);
14854b96380SRuslan Bukin 	}
14954b96380SRuslan Bukin 
15054b96380SRuslan Bukin 	mtx_init(&sc->mtx, device_get_nameunit(dev), "SCMI", MTX_DEF);
15154b96380SRuslan Bukin 
15254b96380SRuslan Bukin 	simplebus_init(dev, node);
15354b96380SRuslan Bukin 
15454b96380SRuslan Bukin 	/*
15554b96380SRuslan Bukin 	 * Allow devices to identify.
15654b96380SRuslan Bukin 	 */
15754b96380SRuslan Bukin 	bus_generic_probe(dev);
15854b96380SRuslan Bukin 
15954b96380SRuslan Bukin 	/*
16054b96380SRuslan Bukin 	 * Now walk the OFW tree and attach top-level devices.
16154b96380SRuslan Bukin 	 */
16254b96380SRuslan Bukin 	for (node = OF_child(node); node > 0; node = OF_peer(node))
16354b96380SRuslan Bukin 		simplebus_add_device(dev, node, 0, NULL, -1, NULL);
16454b96380SRuslan Bukin 
16554b96380SRuslan Bukin 	error = bus_generic_attach(dev);
16654b96380SRuslan Bukin 
16754b96380SRuslan Bukin 	return (error);
16854b96380SRuslan Bukin }
16954b96380SRuslan Bukin 
17054b96380SRuslan Bukin static int
17154b96380SRuslan Bukin scmi_detach(device_t dev)
17254b96380SRuslan Bukin {
17354b96380SRuslan Bukin 
17454b96380SRuslan Bukin 	return (0);
17554b96380SRuslan Bukin }
17654b96380SRuslan Bukin 
17754b96380SRuslan Bukin static device_method_t scmi_methods[] = {
17854b96380SRuslan Bukin 	DEVMETHOD(device_attach,	scmi_attach),
17954b96380SRuslan Bukin 	DEVMETHOD(device_detach,	scmi_detach),
180d46f01fdSAndrew Turner 
18154b96380SRuslan Bukin 	DEVMETHOD_END
18254b96380SRuslan Bukin };
18354b96380SRuslan Bukin 
18454b96380SRuslan Bukin DEFINE_CLASS_1(scmi, scmi_driver, scmi_methods, sizeof(struct scmi_softc),
18554b96380SRuslan Bukin     simplebus_driver);
18654b96380SRuslan Bukin 
18754b96380SRuslan Bukin DRIVER_MODULE(scmi, simplebus, scmi_driver, 0, 0);
18854b96380SRuslan Bukin MODULE_VERSION(scmi, 1);
189