Download raw body.
scmi(4): add mbox transport and perf protocol
> Date: Tue, 19 Nov 2024 14:20:12 +0100
> From: Mark Kettenis <mark.kettenis@xs4all.nl>
>
> > Date: Mon, 18 Nov 2024 01:01:11 +0100
> > From: Tobias Heider <tobias.heider@stusta.de>
> >
> > Here is the promised diff to get scmi for cpu frequency management
> > working on the Snapdragon X Elite. Roughly speaking this adds two
> > big new features: mailbox transport and the scmi perf protocol.
> >
> > Mailbox transport is very similar to smc which we already support.
> > Messages are written to a shared memory region, instead of an SMC call we
> > use a mailbox doorbell to notify the platform that our request is ready.
> > On X Elites this is done using the CPUCP mailbox which we support via
> > qccpucp(4). The attach function for the mbox transport is deferred to make
> > sure our mailbox driver is available when we need it.
> > There are a lot of possible optimization here, instead of polling we could
> > use mailbox interrupts and for PERFORMANCE_LEVEL_GET we probably want to
> > switch to using a fast channel at some point.
> >
> > The perf protocol allows us to read and set the performance level of our
> > performance domains. On the X Elite we have 3 domains, each spanning 4 cpu
> > cores. This patch adds sensors to expose the current frequency and power
> > consumption for each domain. It is a bit pointless since the level doesn't
> > really change currently. Eventually we probably want to hook this up to
> > cpu(4) to expose it to apm(8).
> >
> > The output on my T14s looks like this:
> > $ sysctl | grep scmi
> > hw.sensors.scmi0.power0=0.22 W
> > hw.sensors.scmi0.power1=0.29 W
> > hw.sensors.scmi0.power2=0.31 W
> > hw.sensors.scmi0.frequency0=2976000000.00 Hz
> > hw.sensors.scmi0.frequency1=3417600000.00 Hz
> > hw.sensors.scmi0.frequency2=3417600000.00 Hz
> >
> > Test feedback and reviews welcome. I don't have a machine using scmi-smc or
> > the clock protocol so I'd appreciate if someone could test those too.
>
> A first drive-by review. I need to test this on some hardware that
> implements scmi-smc before I give an ok. But it basically looks ok.
The rk3588 isn't happy with this. I'll investigate...
> > diff a921796a245d04e3f9e9aeb92b328e0f67f5f697 8fdeb116002858bf25bd6d5171e9e91c647099f2
> > commit - a921796a245d04e3f9e9aeb92b328e0f67f5f697
> > commit + 8fdeb116002858bf25bd6d5171e9e91c647099f2
> > blob - 5c567e567920508ecda4cc718168b85cc356898b
> > blob + ac6c78a53ee5f7369835f267aff83d11dc9fed02
> > --- sys/dev/fdt/scmi.c
> > +++ sys/dev/fdt/scmi.c
> > @@ -2,6 +2,7 @@
> >
> > /*
> > * Copyright (c) 2023 Mark Kettenis <kettenis@openbsd.org>
> > + * Copyright (c) 2024 Tobias Heider <tobhe@openbsd.org>
> > *
> > * Permission to use, copy, modify, and distribute this software for any
> > * purpose with or without fee is hereby granted, provided that the above
> > @@ -19,12 +20,16 @@
> > #include <sys/param.h>
> > #include <sys/device.h>
> > #include <sys/systm.h>
> > +#include <sys/malloc.h>
> > +#include <sys/sensors.h>
> > +#include <sys/sysctl.h>
> >
> > #include <machine/bus.h>
> > #include <machine/fdt.h>
> >
> > #include <dev/ofw/openfirm.h>
> > #include <dev/ofw/ofw_clock.h>
> > +#include <dev/ofw/ofw_misc.h>
> > #include <dev/ofw/fdt.h>
> >
> > #include <dev/fdt/pscivar.h>
> > @@ -49,6 +54,7 @@ struct scmi_shmem {
> >
> > /* Protocols */
> > #define SCMI_BASE 0x10
> > +#define SCMI_PERF 0x13
> > #define SCMI_CLOCK 0x14
> >
> > /* Common messages */
> > @@ -64,6 +70,24 @@ struct scmi_shmem {
> > #define SCMI_CLOCK_CONFIG_SET 0x7
> > #define SCMI_CLOCK_CONFIG_SET_ENABLE (1 << 0)
> >
> > +/* Performance management messages */
> > +#define SCMI_PERF_DOMAIN_ATTRIBUTES 0x3
> > +#define SCMI_PERF_DESCRIBE_LEVELS 0x4
> > +#define SCMI_PERF_LEVEL_GET 0x8
> > +
> > +struct scmi_40_resp_perf_describe_levels {
>
> This is version 4.0 of the performance domain management protocol, so
> perhaps scmi_resp_perf_describe_levels_40 is a better name?
>
> > + uint16_t pl_nret;
> > + uint16_t pl_nrem;
> > + struct {
> > + uint32_t pe_perf;
> > + uint32_t pe_cost;
> > + uint16_t pe_latency;
> > + uint16_t pe_reserved;
> > + uint32_t pe_ifreq;
> > + uint32_t pe_lindex;
> > + } pl_entry[];
> > +};
> > +
> > static inline void
> > scmi_message_header(volatile struct scmi_shmem *shmem,
> > uint32_t protocol_id, uint32_t message_id)
> > @@ -71,20 +95,62 @@ scmi_message_header(volatile struct scmi_shmem *shmem,
> > shmem->message_header = (protocol_id << 10) | (message_id << 0);
> > }
> >
> > +struct scmi_ops {
> > + int32_t (*so_command)(void *);
> > +};
>
> Having a separate struct for this seems a bit overkill. Do you think
> we'll add more ops in the near future?
>
> Also, why "void *" and not "struct scmi_softc *" as the argument?
>
> > +struct scmi_perf_level {
> > + uint32_t pl_perf;
> > + uint32_t pl_cost;
> > + uint32_t pl_ifreq;
> > +};
> > +
> > +struct scmi_perf_domain {
> > + size_t pd_nlevels;
> > + struct scmi_perf_level *pd_levels;
> > + int pd_curlevel;
> > +};
> > +
> > struct scmi_softc {
> > struct device sc_dev;
> > bus_space_tag_t sc_iot;
> > - bus_space_handle_t sc_ioh;
> > - volatile struct scmi_shmem *sc_shmem;
> > + int sc_node;
> >
> > + bus_space_handle_t sc_ioh_tx;
> > + bus_space_handle_t sc_ioh_rx;
> > + volatile struct scmi_shmem *sc_shmem_tx;
> > + volatile struct scmi_shmem *sc_shmem_rx;
> > +
> > uint32_t sc_smc_id;
> > + struct mbox_channel *sc_mc_tx;
> > + struct mbox_channel *sc_mc_rx;
> >
> > + uint16_t sc_ver_major;
> > + uint16_t sc_ver_minor;
> > +
> > + /* SCMI_CLOCK */
> > struct clock_device sc_cd;
> > +
> > + /* SCMI_PERF */
> > + int sc_perf_power_unit;
> > +#define SCMI_POWER_UNIT_UW 0x2
> > +#define SCMI_POWER_UNIT_MW 0x1
> > +#define SCMI_POWER_UNIT_NONE 0x0
> > + size_t sc_perf_ndomains;
> > + struct scmi_perf_domain *sc_perf_domains;
> > +
> > + struct ksensordev sc_perf_sensordev;
> > + struct ksensordev sc_perf_psensordev;
> > + struct ksensor *sc_perf_fsensors;
> > + struct ksensor *sc_perf_psensors;
> > +
> > + struct scmi_ops sc_so;
> > };
> >
> > int scmi_match(struct device *, void *, void *);
> > void scmi_attach(struct device *, struct device *, void *);
> > +int scmi_attach_smc(struct scmi_softc *, struct fdt_attach_args *);
> > +void scmi_attach_mbox_deferred(struct device *);
> >
> > const struct cfattach scmi_ca = {
> > sizeof(struct scmi_softc), scmi_match, scmi_attach
> > @@ -96,22 +162,42 @@ struct cfdriver scmi_cd = {
> >
> > void scmi_attach_proto(struct scmi_softc *, int);
> > void scmi_attach_clock(struct scmi_softc *, int);
> > -int32_t scmi_command(struct scmi_softc *);
> > +void scmi_attach_perf(struct scmi_softc *, int);
> >
> > +int32_t scmi_smc_command(void *);
> > +int32_t scmi_mbox_command(void *);
> > +
> > int
> > scmi_match(struct device *parent, void *match, void *aux)
> > {
> > struct fdt_attach_args *faa = aux;
> >
> > - return OF_is_compatible(faa->fa_node, "arm,scmi-smc");
> > + return OF_is_compatible(faa->fa_node, "arm,scmi-smc") ||
> > + OF_is_compatible(faa->fa_node, "arm,scmi");
> > }
> >
> > void
> > scmi_attach(struct device *parent, struct device *self, void *aux)
> > {
> > struct scmi_softc *sc = (struct scmi_softc *)self;
> > + struct fdt_attach_args *faa = aux;
> > +
> > + sc->sc_iot = faa->fa_iot;
> > + sc->sc_node = faa->fa_node;
> > +
> > + if (OF_is_compatible(faa->fa_node, "arm,scmi-scm")) {
> > + scmi_attach_smc(sc, faa);
> > + } else if (OF_is_compatible(faa->fa_node, "arm,scmi")) {
> > + printf("\n");
> > + /* Defer because we need the mailbox driver attached first */
> > + config_defer(self, scmi_attach_mbox_deferred);
> > + }
> > +}
> > +
> > +int
> > +scmi_attach_smc(struct scmi_softc *sc, struct fdt_attach_args *faa)
> > +{
> > volatile struct scmi_shmem *shmem;
> > - struct fdt_attach_args *faa = aux;
> > struct fdt_reg reg;
> > int32_t status;
> > uint32_t version;
> > @@ -119,53 +205,143 @@ scmi_attach(struct device *parent, struct device *self
> > void *node;
> > int proto;
> >
> > - phandle = OF_getpropint(faa->fa_node, "shmem", 0);
> > - node = fdt_find_phandle(phandle);
> > - if (node == NULL || !fdt_is_compatible(node, "arm,scmi-shmem") ||
> > - fdt_get_reg(node, 0, ®)) {
> > - printf(": no shared memory\n");
> > - return;
> > - }
> > -
> > sc->sc_smc_id = OF_getpropint(faa->fa_node, "arm,smc-id", 0);
> > if (sc->sc_smc_id == 0) {
> > printf(": no SMC id\n");
> > - return;
> > + return -1;
> > }
> >
> > - sc->sc_iot = faa->fa_iot;
> > + phandle = OF_getpropint(faa->fa_node, "shmem", 0);
> > + node = fdt_find_phandle(phandle);
> > + if (node == NULL || !fdt_is_compatible(node, "arm,scmi-shmem") ||
> > + fdt_get_reg(node, 0, ®)) {
> > + printf(": no shared memory\n");
> > + return -1;
> > + }
> > +
> > if (bus_space_map(sc->sc_iot, reg.addr,
> > - reg.size, 0, &sc->sc_ioh)) {
> > + reg.size, 0, &sc->sc_ioh_tx)) {
> > printf(": can't map shared memory\n");
> > - return;
> > + return -1;
> > }
> > - sc->sc_shmem = bus_space_vaddr(sc->sc_iot, sc->sc_ioh);
> > - shmem = sc->sc_shmem;
> > + sc->sc_shmem_tx = bus_space_vaddr(sc->sc_iot, sc->sc_ioh_tx);
> > + shmem = sc->sc_shmem_tx;
> >
> > + sc->sc_so.so_command = scmi_smc_command;
> > +
> > if ((shmem->channel_status & SCMI_CHANNEL_FREE) == 0) {
> > printf(": channel busy\n");
> > - return;
> > + return -1;
> > }
> >
> > scmi_message_header(shmem, SCMI_BASE, SCMI_PROTOCOL_VERSION);
> > shmem->length = sizeof(uint32_t);
> > - status = scmi_command(sc);
> > + status = sc->sc_so.so_command(sc);
> > if (status != SCMI_SUCCESS) {
> > printf(": protocol version command failed\n");
> > - return;
> > + return -1;
> > }
> >
> > version = shmem->message_payload[1];
> > - printf(": SCMI %d.%d\n", version >> 16, version & 0xffff);
> > + sc->sc_ver_major = version >> 16;
> > + sc->sc_ver_minor = version & 0xfffff;
> > + printf(": SCMI %d.%d\n", sc->sc_ver_major, sc->sc_ver_minor);
> >
> > for (proto = OF_child(faa->fa_node); proto; proto = OF_peer(proto))
> > scmi_attach_proto(sc, proto);
> > +
> > + return 0;
> > }
> >
> > +void
> > +scmi_attach_mbox_deferred(struct device *self)
> > +{
> > + struct scmi_softc *sc = (struct scmi_softc *)self;
> > + uint32_t *shmems;
> > + int32_t status;
> > + uint32_t version;
> > + struct fdt_reg reg;
> > + int len;
> > + void *node;
> > + int proto;
> > +
> > + /* we only support the 2 mbox / 2 shmem case */
> > + len = OF_getproplen(sc->sc_node, "mboxes");
> > + if (len != 4 * sizeof(uint32_t)) {
> > + printf("%s: invalid number of mboxes\n", sc->sc_dev.dv_xname);
> > + return;
> > + }
> > +
> > + len = OF_getproplen(sc->sc_node, "shmem");
> > + if (len != 2 * sizeof(uint32_t)) {
> > + printf("%s: invalid number of shmems\n", sc->sc_dev.dv_xname);
> > + return;
> > + }
> > +
> > + shmems = malloc(len, M_DEVBUF, M_WAITOK);
> > + OF_getpropintarray(sc->sc_node, "shmem", shmems, len);
> > +
> > + sc->sc_mc_tx = mbox_channel(sc->sc_node, "tx", NULL);
> > + if (sc->sc_mc_tx == NULL) {
> > + printf("%s: no tx mbox\n", sc->sc_dev.dv_xname);
> > + return;
> > + }
> > + sc->sc_mc_rx = mbox_channel(sc->sc_node, "rx", NULL);
> > + if (sc->sc_mc_rx == NULL) {
> > + printf("%s: no rx mbox\n", sc->sc_dev.dv_xname);
> > + return;
> > + }
> > +
> > + node = fdt_find_phandle(shmems[0]);
> > + if (node == NULL || !fdt_is_compatible(node, "arm,scmi-shmem") ||
> > + fdt_get_reg(node, 0, ®)) {
> > + printf("%s: no shared memory\n", sc->sc_dev.dv_xname);
> > + return;
> > + }
> > + if (bus_space_map(sc->sc_iot, reg.addr, reg.size, 0, &sc->sc_ioh_tx)) {
> > + printf("%s: can't map shared memory\n", sc->sc_dev.dv_xname);
> > + return;
> > + }
> > + sc->sc_shmem_tx = bus_space_vaddr(sc->sc_iot, sc->sc_ioh_tx);
> > +
> > + node = fdt_find_phandle(shmems[1]);
> > + if (node == NULL || !fdt_is_compatible(node, "arm,scmi-shmem") ||
> > + fdt_get_reg(node, 0, ®)) {
> > + printf("%s: no shared memory\n", sc->sc_dev.dv_xname);
> > + return;
> > + }
> > + if (bus_space_map(sc->sc_iot, reg.addr, reg.size, 0, &sc->sc_ioh_rx)) {
> > + printf("%s: can't map shared memory\n", sc->sc_dev.dv_xname);
> > + return;
> > + }
> > + sc->sc_shmem_rx = bus_space_vaddr(sc->sc_iot, sc->sc_ioh_rx);
> > +
> > + sc->sc_so.so_command = scmi_mbox_command;
> > +
> > + scmi_message_header(sc->sc_shmem_tx, SCMI_BASE, SCMI_PROTOCOL_VERSION);
> > + sc->sc_shmem_tx->length = sizeof(uint32_t);
> > + status = sc->sc_so.so_command(sc);
> > + if (status != SCMI_SUCCESS) {
> > + printf("%s: protocol version command failed\n",
> > + sc->sc_dev.dv_xname);
> > + return;
> > + }
> > +
> > + version = sc->sc_shmem_tx->message_payload[1];
> > + sc->sc_ver_major = version >> 16;
> > + sc->sc_ver_minor = version & 0xfffff;
> > + printf("%s: SCMI %d.%d\n", sc->sc_dev.dv_xname, sc->sc_ver_major,
> > + sc->sc_ver_minor);
> > +
> > + for (proto = OF_child(sc->sc_node); proto; proto = OF_peer(proto))
> > + scmi_attach_proto(sc, proto);
> > +}
> > +
> > int32_t
> > -scmi_command(struct scmi_softc *sc)
> > +scmi_smc_command(void *arg)
> > {
> > - volatile struct scmi_shmem *shmem = sc->sc_shmem;
> > + struct scmi_softc *sc = arg;
> > + volatile struct scmi_shmem *shmem = sc->sc_shmem_tx;
> > int32_t status;
> >
> > shmem->channel_status = 0;
> > @@ -179,6 +355,33 @@ scmi_command(struct scmi_softc *sc)
> > return shmem->message_payload[0];
> > }
> >
> > +int32_t
> > +scmi_mbox_command(void *arg)
> > +{
> > + struct scmi_softc *sc = arg;
> > + volatile struct scmi_shmem *shmem = sc->sc_shmem_tx;
> > + int ret;
> > + int i;
> > +
> > + shmem->channel_status = 0;
> > + ret = mbox_send(sc->sc_mc_tx, NULL, 0);
> > + if (ret != 0)
> > + return SCMI_NOT_SUPPORTED;
> > +
> > + /* XXX: poll for now */
> > + for (i = 0; i < 20; i++) {
> > + if (shmem->channel_status & SCMI_CHANNEL_FREE)
> > + break;
> > + delay(10);
> > + }
> > + if ((shmem->channel_status & SCMI_CHANNEL_ERROR))
> > + return SCMI_COMMS_ERROR;
> > + if ((shmem->channel_status & SCMI_CHANNEL_FREE) == 0)
> > + return SCMI_BUSY;
> > +
> > + return shmem->message_payload[0];
> > +}
> > +
> > void
> > scmi_attach_proto(struct scmi_softc *sc, int node)
> > {
> > @@ -186,6 +389,9 @@ scmi_attach_proto(struct scmi_softc *sc, int node)
> > case SCMI_CLOCK:
> > scmi_attach_clock(sc, node);
> > break;
> > + case SCMI_PERF:
> > + scmi_attach_perf(sc, node);
> > + break;
> > default:
> > break;
> > }
> > @@ -200,13 +406,13 @@ int scmi_clock_set_frequency(void *, uint32_t *, uint3
> > void
> > scmi_attach_clock(struct scmi_softc *sc, int node)
> > {
> > - volatile struct scmi_shmem *shmem = sc->sc_shmem;
> > + volatile struct scmi_shmem *shmem = sc->sc_shmem_tx;
> > int32_t status;
> > int nclocks;
> >
> > scmi_message_header(shmem, SCMI_CLOCK, SCMI_PROTOCOL_ATTRIBUTES);
> > shmem->length = sizeof(uint32_t);
> > - status = scmi_command(sc);
> > + status = sc->sc_so.so_command(sc);
> > if (status != SCMI_SUCCESS)
> > return;
> >
> > @@ -226,28 +432,28 @@ void
> > scmi_clock_enable(void *cookie, uint32_t *cells, int on)
> > {
> > struct scmi_softc *sc = cookie;
> > - volatile struct scmi_shmem *shmem = sc->sc_shmem;
> > + volatile struct scmi_shmem *shmem = sc->sc_shmem_tx;
> > uint32_t idx = cells[0];
> >
> > scmi_message_header(shmem, SCMI_CLOCK, SCMI_CLOCK_CONFIG_SET);
> > shmem->length = 3 * sizeof(uint32_t);
> > shmem->message_payload[0] = idx;
> > shmem->message_payload[1] = on ? SCMI_CLOCK_CONFIG_SET_ENABLE : 0;
> > - scmi_command(sc);
> > + sc->sc_so.so_command(sc);
> > }
> >
> > uint32_t
> > scmi_clock_get_frequency(void *cookie, uint32_t *cells)
> > {
> > struct scmi_softc *sc = cookie;
> > - volatile struct scmi_shmem *shmem = sc->sc_shmem;
> > + volatile struct scmi_shmem *shmem = sc->sc_shmem_tx;
> > uint32_t idx = cells[0];
> > int32_t status;
> >
> > scmi_message_header(shmem, SCMI_CLOCK, SCMI_CLOCK_RATE_GET);
> > shmem->length = 2 * sizeof(uint32_t);
> > shmem->message_payload[0] = idx;
> > - status = scmi_command(sc);
> > + status = sc->sc_so.so_command(sc);
> > if (status != SCMI_SUCCESS)
> > return 0;
> > if (shmem->message_payload[2] != 0)
> > @@ -260,7 +466,7 @@ int
> > scmi_clock_set_frequency(void *cookie, uint32_t *cells, uint32_t freq)
> > {
> > struct scmi_softc *sc = cookie;
> > - volatile struct scmi_shmem *shmem = sc->sc_shmem;
> > + volatile struct scmi_shmem *shmem = sc->sc_shmem_tx;
> > uint32_t idx = cells[0];
> > int32_t status;
> >
> > @@ -270,9 +476,188 @@ scmi_clock_set_frequency(void *cookie, uint32_t *cells
> > shmem->message_payload[1] = idx;
> > shmem->message_payload[2] = freq;
> > shmem->message_payload[3] = 0;
> > - status = scmi_command(sc);
> > + status = sc->sc_so.so_command(sc);
> > if (status != SCMI_SUCCESS)
> > return -1;
> >
> > return 0;
> > }
> > +
> > +/* Performance management */
> > +void scmi_perf_descr_levels(struct scmi_softc *, int);
> > +void scmi_perf_refresh_sensor(void *);
> > +
> > +void
> > +scmi_attach_perf(struct scmi_softc *sc, int node)
> > +{
> > + volatile struct scmi_shmem *shmem = sc->sc_shmem_tx;
> > + int32_t status;
> > + uint32_t version;
> > + int i;
> > +
> > + scmi_message_header(sc->sc_shmem_tx, SCMI_PERF, SCMI_PROTOCOL_VERSION);
> > + sc->sc_shmem_tx->length = sizeof(uint32_t);
> > + status = sc->sc_so.so_command(sc);
> > + if (status != SCMI_SUCCESS) {
> > + printf("%s: SCMI_PROTOCOL_VERSION failed\n",
> > + sc->sc_dev.dv_xname);
> > + return;
> > + }
> > +
> > + version = shmem->message_payload[1];
> > + if (version != 0x40000) {
> > + printf("%s: invalid perf protocol version (0x%x != 0x4000)",
> > + sc->sc_dev.dv_xname, version);
> > + return;
> > + }
> > +
> > + scmi_message_header(shmem, SCMI_PERF, SCMI_PROTOCOL_ATTRIBUTES);
> > + shmem->length = sizeof(uint32_t);
> > + status = sc->sc_so.so_command(sc);
> > + if (status != SCMI_SUCCESS) {
> > + printf("%s: SCMI_PROTOCOL_ATTRIBUTES failed\n",
> > + sc->sc_dev.dv_xname);
> > + return;
> > + }
> > +
> > + sc->sc_perf_ndomains = shmem->message_payload[1] & 0xffff;
> > + sc->sc_perf_domains = malloc(sc->sc_perf_ndomains *
> > + sizeof(struct scmi_perf_domain), M_DEVBUF, M_ZERO | M_WAITOK);
> > + sc->sc_perf_power_unit = (shmem->message_payload[1] >> 16) & 0x3;
> > +
> > + strlcpy(sc->sc_perf_sensordev.xname, sc->sc_dev.dv_xname,
> > + sizeof(sc->sc_perf_sensordev.xname));
> > +
> > + sc->sc_perf_fsensors =
> > + malloc(sc->sc_perf_ndomains * sizeof(struct ksensor),
> > + M_DEVBUF, M_ZERO | M_WAITOK);
> > + sc->sc_perf_psensors =
> > + malloc(sc->sc_perf_ndomains * sizeof(struct ksensor),
> > + M_DEVBUF, M_ZERO | M_WAITOK);
> > +
> > + /* Add one frequency sensor per perf domain */
> > + for (i = 0; i < sc->sc_perf_ndomains; i++) {
> > + scmi_message_header(shmem, SCMI_PERF,
> > + SCMI_PERF_DOMAIN_ATTRIBUTES);
> > + shmem->length = 2 * sizeof(uint32_t);
> > + shmem->message_payload[0] = i;
> > + status = sc->sc_so.so_command(sc);
> > + if (status != SCMI_SUCCESS) {
> > + printf("%s: SCMI_PERF_DOMAIN_ATTRIBUTES failed\n",
> > + sc->sc_dev.dv_xname);
> > + goto err;
> > + }
> > +
> > + scmi_perf_descr_levels(sc, i);
> > +
> > + sc->sc_perf_fsensors[i].type = SENSOR_FREQ;
> > + sensor_attach(&sc->sc_perf_sensordev, &sc->sc_perf_fsensors[i]);
> > + sc->sc_perf_psensors[i].type = SENSOR_WATTS;
> > + sensor_attach(&sc->sc_perf_sensordev, &sc->sc_perf_psensors[i]);
> > + }
> > + sensordev_install(&sc->sc_perf_sensordev);
> > + sensor_task_register(sc, scmi_perf_refresh_sensor, 1);
> > + return;
> > +err:
> > + free(sc->sc_perf_fsensors, M_DEVBUF,
> > + sc->sc_perf_ndomains * sizeof(struct ksensor));
> > + free(sc->sc_perf_psensors, M_DEVBUF,
> > + sc->sc_perf_ndomains * sizeof(struct ksensor));
> > +}
> > +
> > +void
> > +scmi_perf_descr_levels(struct scmi_softc *sc, int domain)
> > +{
> > + volatile struct scmi_shmem *shmem = sc->sc_shmem_tx;
> > + volatile struct scmi_40_resp_perf_describe_levels *pl;
> > + struct scmi_perf_domain *pd = &sc->sc_perf_domains[domain];
> > + int status, i, idx;
> > +
> > + idx = 0;
> > + do {
> > + scmi_message_header(shmem, SCMI_PERF,
> > + SCMI_PERF_DESCRIBE_LEVELS);
> > + shmem->length = sizeof(uint32_t) * 3;
> > + shmem->message_payload[0] = domain;
> > + shmem->message_payload[1] = idx;
> > + status = sc->sc_so.so_command(sc);
> > + if (status != SCMI_SUCCESS) {
> > + printf("%s: SCMI_PERF_DESCRIBE_LEVELS failed\n",
> > + sc->sc_dev.dv_xname);
> > + return;
> > + }
> > +
> > + pl = (struct scmi_40_resp_perf_describe_levels *)
> > + &shmem->message_payload[1];
> > +
> > + if (pd->pd_levels == NULL) {
> > + pd->pd_nlevels = pl->pl_nret + pl->pl_nrem;
> > + pd->pd_levels = malloc(pd->pd_nlevels *
> > + sizeof(struct scmi_perf_level),
> > + M_DEVBUF, M_ZERO | M_WAITOK);
> > + }
> > +
> > + for (i = 0; i < pl->pl_nret; i++) {
> > + pd->pd_levels[idx + i].pl_cost =
> > + pl->pl_entry[i].pe_cost;
> > + pd->pd_levels[idx + i].pl_perf =
> > + pl->pl_entry[i].pe_perf;
> > + pd->pd_levels[idx + i].pl_ifreq =
> > + pl->pl_entry[i].pe_ifreq;
> > + }
> > + idx += pl->pl_nret;
> > + } while (pl->pl_nrem);
> > +}
> > +
> > +void
> > +scmi_perf_refresh_sensor(void *arg)
> > +{
> > + struct scmi_softc *sc = arg;
> > + volatile struct scmi_shmem *shmem = sc->sc_shmem_tx;
> > + uint64_t power_cost;
> > + int32_t status;
> > + int level, i;
> > +
> > + if (sc->sc_perf_domains == NULL)
> > + return;
> > +
> > + for (i = 0; i < sc->sc_perf_ndomains; i++) {
> > + if (sc->sc_perf_domains[i].pd_levels == NULL)
> > + return;
> > +
> > + scmi_message_header(shmem, SCMI_PERF,
> > + SCMI_PERF_LEVEL_GET);
> > + shmem->length = sizeof(uint32_t) * 2;
> > + shmem->message_payload[0] = i;
> > + status = sc->sc_so.so_command(sc);
> > + if (status != SCMI_SUCCESS) {
> > + printf("%s: SCMI_PERF_LEVEL_GET failed\n",
> > + sc->sc_dev.dv_xname);
> > + return;
> > + }
> > +
> > + level = shmem->message_payload[1];
> > + if (sc->sc_perf_fsensors == NULL ||
> > + sc->sc_perf_psensors == NULL)
> > + return;
> > +
> > + sc->sc_perf_domains[i].pd_curlevel = level;
> > + sc->sc_perf_fsensors[i].value =
> > + (uint64_t)sc->sc_perf_domains[i].
> > + pd_levels[level].pl_ifreq * 1000000000;
> > +
> > + switch (sc->sc_perf_power_unit) {
> > + case SCMI_POWER_UNIT_UW:
> > + power_cost = (uint64_t)sc->sc_perf_domains[i].
> > + pd_levels[level].pl_cost;
> > + break;
> > + case SCMI_POWER_UNIT_MW:
> > + power_cost = (uint64_t)sc->sc_perf_domains[i].
> > + pd_levels[level].pl_cost * 1000;
> > + break;
> > + default:
> > + continue;
> > + }
> > + sc->sc_perf_psensors[i].value = power_cost;
> > + }
> > +}
> >
> >
>
>
scmi(4): add mbox transport and perf protocol