Skip to content

Commit

Permalink
scmi: Introduce a new SCMI API and port CLK SCMI driver to it
Browse files Browse the repository at this point in the history
Expose new scmi_buf_get/put API methods to build and send messages;
command request descriptors are now pre-allocated when the SCMI core is
initialized and kept in a free list, instead of being allocated on the
stack of the caller of the SCMI request.

Dynamically allocated descriptors enable the SCMI core to keep around
and track outstanding transactions for as long as needed, outliving the
lifetime of the caller stack: this allows tracking of late or missing
replies and it will be needed when adding support for SCMI transports
that allows for more messages to be inflight concurrently.

Move the existing CLK SCMI driver to the new API.

Reviewed by:	andrew
Tested on:	Arm Morello Board
Sponsored by:	Arm Ltd
Differential Revision:	https://reviews.freebsd.org/D43046
  • Loading branch information
freefall75 authored and zxombie committed Apr 11, 2024
1 parent 3595f18 commit 35f9320
Show file tree
Hide file tree
Showing 9 changed files with 433 additions and 249 deletions.
282 changes: 264 additions & 18 deletions sys/dev/firmware/arm/scmi.c

Large diffs are not rendered by default.

32 changes: 14 additions & 18 deletions sys/dev/firmware/arm/scmi.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,10 @@

#include "scmi_if.h"


#define dprintf(fmt, ...)

#define SCMI_MAX_MSG 32
#define SCMI_MSG_HDR_SIZE (sizeof(uint32_t))
#define SCMI_MAX_MSG_PAYLD_SIZE 128
#define SCMI_MAX_MSG_REPLY_SIZE (SCMI_MAX_MSG_PAYLD_SIZE - sizeof(uint32_t))
#define SCMI_MAX_MSG_SIZE (SCMI_MAX_MSG_PAYLD_SIZE + sizeof(uint32_t))

enum scmi_chan {
SCMI_CHAN_A2P,
Expand All @@ -61,26 +60,23 @@ struct scmi_softc {
struct scmi_transport *trs;
};

struct scmi_req {
bool use_polling;
bool done;
LIST_ENTRY(scmi_req) next;
int protocol_id;
int message_id;
int token;
uint32_t msg_header;
const void *in_buf;
uint32_t in_size;
void *out_buf;
uint32_t out_size;
struct scmi_msg {
bool polling;
uint32_t tx_len;
uint32_t rx_len;
#define SCMI_MSG_HDR_SIZE (sizeof(uint32_t))
uint32_t hdr;
uint8_t payld[];
};

int scmi_request(device_t dev, struct scmi_req *req);
void *scmi_buf_get(device_t dev, uint8_t protocol_id, uint8_t message_id,
int tx_payd_sz, int rx_payld_sz);
void scmi_buf_put(device_t dev, void *buf);
int scmi_request(device_t dev, void *in, void **);
void scmi_rx_irq_callback(device_t dev, void *chan, uint32_t hdr);

DECLARE_CLASS(scmi_driver);

int scmi_attach(device_t dev);
int scmi_request(device_t dev, struct scmi_req *req);

#endif /* !_ARM64_SCMI_SCMI_H_ */
232 changes: 99 additions & 133 deletions sys/dev/firmware/arm/scmi_clk.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2022 Ruslan Bukin <[email protected]>
* Copyright (c) 2023 Arm Ltd
*
* This work was supported by Innovate UK project 105694, "Digital Security
* by Design (DSbD) Technology Platform Prototype".
Expand Down Expand Up @@ -58,88 +59,68 @@ struct scmi_clknode_softc {
static int
scmi_clk_get_rate(struct scmi_clk_softc *sc, int clk_id, uint64_t *rate)
{
struct scmi_clk_rate_get_out out;
struct scmi_clk_rate_get_in in;
struct scmi_req req;
struct scmi_clk_rate_get_out *out;
struct scmi_clk_rate_get_in *in;
int error;

req.protocol_id = SCMI_PROTOCOL_ID_CLOCK;
req.message_id = SCMI_CLOCK_RATE_GET;
req.in_buf = &in;
req.in_size = sizeof(struct scmi_clk_rate_get_in);
req.out_buf = &out;
req.out_size = sizeof(struct scmi_clk_rate_get_out);

in.clock_id = clk_id;

error = scmi_request(sc->scmi, &req);
if (error != 0)
return (error);

if (out.status != 0)
in = scmi_buf_get(sc->scmi, SCMI_PROTOCOL_ID_CLOCK,
SCMI_CLOCK_RATE_GET, sizeof(*in), sizeof(*out));
if (in == NULL)
return (ENXIO);

*rate = out.rate_lsb | ((uint64_t)out.rate_msb << 32);
in->clock_id = clk_id;
error = scmi_request(sc->scmi, in, (void **)&out);
if (error == 0)
*rate = out->rate_lsb | ((uint64_t)out->rate_msb << 32);

return (0);
scmi_buf_put(sc->scmi, in);

return (error);
}

static int
scmi_clk_set_rate(struct scmi_clk_softc *sc, int clk_id, uint64_t rate)
{
struct scmi_clk_rate_set_out out;
struct scmi_clk_rate_set_in in;
struct scmi_req req;
struct scmi_clk_rate_set_in *in;
void *out;
int error;

req.protocol_id = SCMI_PROTOCOL_ID_CLOCK;
req.message_id = SCMI_CLOCK_RATE_SET;
req.in_buf = &in;
req.in_size = sizeof(struct scmi_clk_rate_set_in);
req.out_buf = &out;
req.out_size = sizeof(struct scmi_clk_rate_set_out);
in = scmi_buf_get(sc->scmi, SCMI_PROTOCOL_ID_CLOCK,
SCMI_CLOCK_RATE_SET, sizeof(*in), 0);
if (in == NULL)
return (ENXIO);

in.clock_id = clk_id;
in.flags = SCMI_CLK_RATE_ROUND_CLOSEST;
in.rate_lsb = (uint32_t)rate;
in.rate_msb = (uint32_t)(rate >> 32);
in->clock_id = clk_id;
in->flags = SCMI_CLK_RATE_ROUND_CLOSEST;
in->rate_lsb = (uint32_t)rate;
in->rate_msb = (uint32_t)(rate >> 32);

error = scmi_request(sc->scmi, &req);
if (error != 0)
return (error);
error = scmi_request(sc->scmi, in, &out);

if (out.status != 0)
return (ENXIO);
scmi_buf_put(sc->scmi, in);

return (0);
return (error);
}

static int __unused
scmi_clk_gate(struct scmi_clk_softc *sc, int clk_id, int enable)
{
struct scmi_clk_state_out out;
struct scmi_clk_state_in in;
struct scmi_req req;
struct scmi_clk_state_in *in;
void *out;
int error;

req.protocol_id = SCMI_PROTOCOL_ID_CLOCK;
req.message_id = SCMI_CLOCK_CONFIG_SET;
req.in_buf = &in;
req.in_size = sizeof(struct scmi_clk_state_in);
req.out_buf = &out;
req.out_size = sizeof(struct scmi_clk_state_out);

in.clock_id = clk_id;
in.attributes = enable;
in = scmi_buf_get(sc->scmi, SCMI_PROTOCOL_ID_CLOCK,
SCMI_CLOCK_CONFIG_SET, sizeof(*in), 0);
if (in == NULL)
return (ENXIO);

error = scmi_request(sc->scmi, &req);
if (error != 0)
return (error);
in->clock_id = clk_id;
in->attributes = enable;
error = scmi_request(sc->scmi, in, &out);

if (out.status != 0)
return (ENXIO);
scmi_buf_put(sc->scmi, in);

return (0);
return (error);
}

static int
Expand Down Expand Up @@ -178,8 +159,6 @@ scmi_clknode_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout,
clk_sc = clknode_get_softc(clk);
sc = device_get_softc(clk_sc->dev);

dprintf("%s: %ld\n", __func__, *fout);

scmi_clk_set_rate(sc, clk_sc->clock_id, *fout);

*stop = 1;
Expand Down Expand Up @@ -235,119 +214,106 @@ scmi_clk_add_node(struct scmi_clk_softc *sc, int index, char *clock_name)
static int
scmi_clk_get_name(struct scmi_clk_softc *sc, int index, char **result)
{
struct scmi_clk_name_get_out out;
struct scmi_clk_name_get_in in;
struct scmi_req req;
char *clock_name;
struct scmi_clk_name_get_out *out;
struct scmi_clk_name_get_in *in;
int error;

req.protocol_id = SCMI_PROTOCOL_ID_CLOCK;
req.message_id = SCMI_CLOCK_NAME_GET;
req.in_buf = &in;
req.in_size = sizeof(struct scmi_clk_name_get_in);
req.out_buf = &out;
req.out_size = sizeof(struct scmi_clk_name_get_out);

in.clock_id = index;

error = scmi_request(sc->scmi, &req);
if (error != 0)
return (error);

if (out.status != 0)
in = scmi_buf_get(sc->scmi, SCMI_PROTOCOL_ID_CLOCK,
SCMI_CLOCK_NAME_GET, sizeof(*in), sizeof(*out));
if (in == NULL)
return (ENXIO);

clock_name = malloc(sizeof(out.name), M_DEVBUF, M_WAITOK);
strncpy(clock_name, out.name, sizeof(out.name));
in->clock_id = index;
error = scmi_request(sc->scmi, in, (void **)&out);
if (error == 0) {
char *clock_name;

*result = clock_name;
clock_name = malloc(sizeof(out->name), M_DEVBUF, M_WAITOK);
strncpy(clock_name, out->name, sizeof(out->name));
*result = clock_name;
}

return (0);
scmi_buf_put(sc->scmi, in);

return (error);
}

static int
scmi_clk_attrs(struct scmi_clk_softc *sc, int index)
{
struct scmi_clk_attrs_out out;
struct scmi_clk_attrs_in in;
struct scmi_req req;
int error;
struct scmi_clk_attrs_out *out;
struct scmi_clk_attrs_in *in;
char *clock_name;
int error;

req.protocol_id = SCMI_PROTOCOL_ID_CLOCK;
req.message_id = SCMI_CLOCK_ATTRIBUTES;
req.in_buf = &in;
req.in_size = sizeof(struct scmi_clk_attrs_in);
req.out_buf = &out;
req.out_size = sizeof(struct scmi_clk_attrs_out);

in.clock_id = index;

error = scmi_request(sc->scmi, &req);
if (error != 0)
return (error);

if (out.status != 0)
in = scmi_buf_get(sc->scmi, SCMI_PROTOCOL_ID_CLOCK,
SCMI_CLOCK_ATTRIBUTES, sizeof(*in), sizeof(*out));
if (in == NULL)
return (ENXIO);

if (out.attributes & CLK_ATTRS_EXT_CLK_NAME) {
error = scmi_clk_get_name(sc, index, &clock_name);
if (error)
return (error);
} else {
clock_name = malloc(sizeof(out.clock_name), M_DEVBUF, M_WAITOK);
strncpy(clock_name, out.clock_name, sizeof(out.clock_name));
in->clock_id = index;
error = scmi_request(sc->scmi, in, (void **)&out);
if (error == 0) {
if (out->attributes & CLK_ATTRS_EXT_CLK_NAME) {
error = scmi_clk_get_name(sc, index, &clock_name);
} else {
clock_name = malloc(sizeof(out->clock_name),
M_DEVBUF, M_WAITOK);
strncpy(clock_name, out->clock_name,
sizeof(out->clock_name));
}

if (error == 0)
error = scmi_clk_add_node(sc, index, clock_name);
}

error = scmi_clk_add_node(sc, index, clock_name);
scmi_buf_put(sc->scmi, in);

return (error);
}

static int
scmi_clk_discover(struct scmi_clk_softc *sc)
{
struct scmi_clk_protocol_attrs_out out;
struct scmi_req req;
struct scmi_clk_protocol_attrs_out *out;
void *in;
int nclocks;
int failing;
int error;
int i;

req.protocol_id = SCMI_PROTOCOL_ID_CLOCK;
req.message_id = SCMI_PROTOCOL_ATTRIBUTES;
req.in_buf = NULL;
req.in_size = 0;
req.out_buf = &out;
req.out_size = sizeof(struct scmi_clk_protocol_attrs_out);

error = scmi_request(sc->scmi, &req);
if (error != 0)
return (error);

if (out.status != 0)
in = scmi_buf_get(sc->scmi, SCMI_PROTOCOL_ID_CLOCK,
SCMI_PROTOCOL_ATTRIBUTES, 0, sizeof(*out));
if (in == NULL)
return (ENXIO);

nclocks = (out.attributes & CLK_ATTRS_NCLOCKS_M) >>
CLK_ATTRS_NCLOCKS_S;
error = scmi_request(sc->scmi, in, (void **)&out);
if (error == 0) {
nclocks = (out->attributes & CLK_ATTRS_NCLOCKS_M) >>
CLK_ATTRS_NCLOCKS_S;

device_printf(sc->dev, "Found %d clocks.\n", nclocks);
device_printf(sc->dev, "Found %d clocks.\n", nclocks);

failing = 0;
failing = 0;

for (i = 0; i < nclocks; i++) {
error = scmi_clk_attrs(sc, i);
if (error) {
device_printf(sc->dev,
"Could not process clock index %d.\n", i);
failing++;
for (i = 0; i < nclocks; i++) {
error = scmi_clk_attrs(sc, i);
if (error) {
device_printf(sc->dev,
"Could not process clock index %d.\n", i);
failing++;
error = 0;
}
}
if (failing == nclocks)
error = ENXIO;
} else {
error = ENXIO;
}

if (failing == nclocks)
return (ENXIO);
scmi_buf_put(sc->scmi, in);

return (0);
return (error);
}

static int
Expand Down
Loading

0 comments on commit 35f9320

Please sign in to comment.