From: Tobias Heider Subject: Re: scmi(4): add mbox transport and perf protocol To: Mark Kettenis Cc: tech@openbsd.org, kettenis@openbsd.org, patrick@openbsd.org, landry@openbsd.org Date: Wed, 20 Nov 2024 22:05:45 +0100 On Tue, Nov 19, 2024 at 10:30:58PM GMT, Mark Kettenis wrote: > > Date: Tue, 19 Nov 2024 22:03:01 +0100 > > From: Mark Kettenis > > > > > 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... > > Turns out to be a simple typo: > > > > > + > > > > + if (OF_is_compatible(faa->fa_node, "arm,scmi-scm")) { > > > > + scmi_attach_smc(sc, faa); > > Should be "arm,scmi-smc". Here is a new version with all the feedback incorporated. Tests look good, works on X Elite, still work on Rockchip. ok? diff a921796a245d04e3f9e9aeb92b328e0f67f5f697 1aba915c8fcaa5bd088fa5d0bf872ef48b3bd3eb commit - a921796a245d04e3f9e9aeb92b328e0f67f5f697 commit + 1aba915c8fcaa5bd088fa5d0bf872ef48b3bd3eb blob - 5c567e567920508ecda4cc718168b85cc356898b blob + f7b1bf68a50fae33b840a317d0311e9d2143a31e --- 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_resp_perf_describe_levels_40 { + 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,58 @@ scmi_message_header(volatile struct scmi_shmem *shmem, shmem->message_header = (protocol_id << 10) | (message_id << 0); } +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; + + int32_t (*sc_command)(struct scmi_softc *); }; 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 +158,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(struct scmi_softc *); +int32_t scmi_mbox_command(struct scmi_softc *); + 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-smc")) { + 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 +201,142 @@ 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_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_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_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_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(struct scmi_softc *sc) { - volatile struct scmi_shmem *shmem = sc->sc_shmem; + volatile struct scmi_shmem *shmem = sc->sc_shmem_tx; int32_t status; shmem->channel_status = 0; @@ -179,6 +350,32 @@ scmi_command(struct scmi_softc *sc) return shmem->message_payload[0]; } +int32_t +scmi_mbox_command(struct scmi_softc *sc) +{ + 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 +383,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 +400,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_command(sc); if (status != SCMI_SUCCESS) return; @@ -226,28 +426,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_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_command(sc); if (status != SCMI_SUCCESS) return 0; if (shmem->message_payload[2] != 0) @@ -260,7 +460,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 +470,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_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_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_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_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_resp_perf_describe_levels_40 *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_command(sc); + if (status != SCMI_SUCCESS) { + printf("%s: SCMI_PERF_DESCRIBE_LEVELS failed\n", + sc->sc_dev.dv_xname); + return; + } + + pl = (struct scmi_resp_perf_describe_levels_40 *) + &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_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; + } +}