From: Mark Kettenis Subject: Re: scmi(4): add mbox transport and perf protocol To: Mark Kettenis Cc: tobias.heider@stusta.de, tech@openbsd.org, kettenis@openbsd.org, patrick@openbsd.org, landry@openbsd.org Date: Tue, 19 Nov 2024 22:03:01 +0100 > Date: Tue, 19 Nov 2024 14:20:12 +0100 > From: Mark Kettenis > > > Date: Mon, 18 Nov 2024 01:01:11 +0100 > > From: Tobias Heider > > > > 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 > > + * Copyright (c) 2024 Tobias Heider > > * > > * 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 > > #include > > #include > > +#include > > +#include > > +#include > > > > #include > > #include > > > > #include > > #include > > +#include > > #include > > > > #include > > @@ -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; > > + } > > +} > > > > > >