Changeset View
Changeset View
Standalone View
Standalone View
sys/dev/firmware/arm/scmi_shmem.c
| /*- | /*- | ||||
| * SPDX-License-Identifier: BSD-2-Clause | * SPDX-License-Identifier: BSD-2-Clause | ||||
| * | * | ||||
| * Copyright (c) 2022 Ruslan Bukin <br@bsdpad.com> | * Copyright (c) 2022 Ruslan Bukin <br@bsdpad.com> | ||||
| * Copyright (c) 2023 Arm Ltd | |||||
| * | * | ||||
| * This work was supported by Innovate UK project 105694, "Digital Security | * This work was supported by Innovate UK project 105694, "Digital Security | ||||
| * by Design (DSbD) Technology Platform Prototype". | * by Design (DSbD) Technology Platform Prototype". | ||||
| * | * | ||||
| * Redistribution and use in source and binary forms, with or without | * Redistribution and use in source and binary forms, with or without | ||||
| * modification, are permitted provided that the following conditions | * modification, are permitted provided that the following conditions | ||||
| * are met: | * are met: | ||||
| * 1. Redistributions of source code must retain the above copyright | * 1. Redistributions of source code must retain the above copyright | ||||
| Show All 23 Lines | |||||
| #include <sys/module.h> | #include <sys/module.h> | ||||
| #include <dev/fdt/simplebus.h> | #include <dev/fdt/simplebus.h> | ||||
| #include <dev/fdt/fdt_common.h> | #include <dev/fdt/fdt_common.h> | ||||
| #include <dev/ofw/ofw_bus_subr.h> | #include <dev/ofw/ofw_bus_subr.h> | ||||
| #include "mmio_sram_if.h" | #include "mmio_sram_if.h" | ||||
| #include "scmi_shmem.h" | |||||
| #include "scmi.h" | #include "scmi.h" | ||||
| struct shmem_softc { | struct shmem_softc { | ||||
| device_t dev; | device_t dev; | ||||
| device_t parent; | device_t parent; | ||||
| int reg; | int reg; | ||||
| }; | }; | ||||
| static void scmi_shmem_read(device_t, bus_size_t, void *, bus_size_t); | |||||
| static void scmi_shmem_write(device_t, bus_size_t, const void *, | |||||
| bus_size_t); | |||||
| static int shmem_probe(device_t); | |||||
| static int shmem_attach(device_t); | |||||
| static int shmem_detach(device_t); | |||||
| static int | static int | ||||
| shmem_probe(device_t dev) | shmem_probe(device_t dev) | ||||
| { | { | ||||
| if (!ofw_bus_is_compatible(dev, "arm,scmi-shmem")) | if (!ofw_bus_is_compatible(dev, "arm,scmi-shmem")) | ||||
| return (ENXIO); | return (ENXIO); | ||||
| if (!ofw_bus_status_okay(dev)) | if (!ofw_bus_status_okay(dev)) | ||||
| Show All 32 Lines | |||||
| static int | static int | ||||
| shmem_detach(device_t dev) | shmem_detach(device_t dev) | ||||
| { | { | ||||
| return (0); | return (0); | ||||
| } | } | ||||
| void | static void | ||||
| scmi_shmem_read(device_t dev, bus_size_t offset, void *buf, bus_size_t len) | scmi_shmem_read(device_t dev, bus_size_t offset, void *buf, bus_size_t len) | ||||
| { | { | ||||
| struct shmem_softc *sc; | struct shmem_softc *sc; | ||||
| uint8_t *addr; | uint8_t *addr; | ||||
| int i; | int i; | ||||
| sc = device_get_softc(dev); | sc = device_get_softc(dev); | ||||
| addr = (uint8_t *)buf; | addr = (uint8_t *)buf; | ||||
| for (i = 0; i < len; i++) | for (i = 0; i < len; i++) | ||||
| addr[i] = MMIO_SRAM_READ_1(sc->parent, sc->reg + offset + i); | addr[i] = MMIO_SRAM_READ_1(sc->parent, sc->reg + offset + i); | ||||
| } | } | ||||
| void | static void | ||||
| scmi_shmem_write(device_t dev, bus_size_t offset, const void *buf, | scmi_shmem_write(device_t dev, bus_size_t offset, const void *buf, | ||||
| bus_size_t len) | bus_size_t len) | ||||
| { | { | ||||
| struct shmem_softc *sc; | struct shmem_softc *sc; | ||||
| const uint8_t *addr; | const uint8_t *addr; | ||||
| int i; | int i; | ||||
| sc = device_get_softc(dev); | sc = device_get_softc(dev); | ||||
| addr = (const uint8_t *)buf; | addr = (const uint8_t *)buf; | ||||
| for (i = 0; i < len; i++) | for (i = 0; i < len; i++) | ||||
| MMIO_SRAM_WRITE_1(sc->parent, sc->reg + offset + i, addr[i]); | MMIO_SRAM_WRITE_1(sc->parent, sc->reg + offset + i, addr[i]); | ||||
| } | |||||
| device_t | |||||
| scmi_shmem_get(device_t dev, phandle_t node, int index) | |||||
| { | |||||
| phandle_t *shmems; | |||||
| device_t shmem_dev; | |||||
| size_t len; | |||||
| len = OF_getencprop_alloc_multi(node, "shmem", sizeof(*shmems), | |||||
| (void **)&shmems); | |||||
| if (len <= 0) { | |||||
| device_printf(dev, "%s: Can't get shmem node.\n", __func__); | |||||
| return (NULL); | |||||
| } | |||||
| if (index >= len) { | |||||
| OF_prop_free(shmems); | |||||
| return (NULL); | |||||
| } | |||||
| shmem_dev = OF_device_from_xref(shmems[index]); | |||||
| if (shmem_dev == NULL) | |||||
| device_printf(dev, "%s: Can't get shmem device.\n", | |||||
| __func__); | |||||
| OF_prop_free(shmems); | |||||
| return (shmem_dev); | |||||
| } | |||||
| int | |||||
| scmi_shmem_prepare_msg(device_t dev, struct scmi_req *req) | |||||
| { | |||||
| struct scmi_smt_header hdr = {}; | |||||
| uint32_t channel_status; | |||||
| /* Read channel status */ | |||||
| scmi_shmem_read(dev, SMT_OFFSET_CHAN_STATUS, &channel_status, | |||||
| SMT_SIZE_CHAN_STATUS); | |||||
| if ((channel_status & SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE) == 0) { | |||||
| device_printf(dev, "Shmem channel busy. Abort !.\n"); | |||||
| return (1); | |||||
| } | |||||
| /* Update header */ | |||||
| hdr.channel_status &= ~SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE; | |||||
| hdr.msg_header = htole32(req->msg_header); | |||||
| hdr.length = htole32(sizeof(req->msg_header) + req->in_size); | |||||
| hdr.flags |= SCMI_SHMEM_FLAG_INTR_ENABLED; | |||||
| /* Write header */ | |||||
| scmi_shmem_write(dev, 0, &hdr, SMT_SIZE_HEADER); | |||||
| /* Write request payload if any */ | |||||
| if (req->in_size) | |||||
| scmi_shmem_write(dev, SMT_SIZE_HEADER, req->in_buf, | |||||
| req->in_size); | |||||
| return (0); | |||||
| } | |||||
| int | |||||
| scmi_shmem_read_msg_header(device_t dev, uint32_t *msg_header) | |||||
| { | |||||
| uint32_t length, header; | |||||
| /* Read and check length. */ | |||||
| scmi_shmem_read(dev, SMT_OFFSET_LENGTH, &length, SMT_SIZE_LENGTH); | |||||
| if (le32toh(length) < sizeof(header)) | |||||
| return (EINVAL); | |||||
| /* Read header. */ | |||||
| scmi_shmem_read(dev, SMT_OFFSET_MSG_HEADER, &header, | |||||
| SMT_SIZE_MSG_HEADER); | |||||
| *msg_header = le32toh(header); | |||||
| return (0); | |||||
| } | |||||
| int | |||||
| scmi_shmem_read_msg_payload(device_t dev, uint8_t *buf, uint32_t buf_len) | |||||
| { | |||||
| uint32_t length, payld_len; | |||||
| /* Read length. */ | |||||
| scmi_shmem_read(dev, SMT_OFFSET_LENGTH, &length, SMT_SIZE_LENGTH); | |||||
| payld_len = le32toh(length) - SCMI_MSG_HDR_SIZE; | |||||
| if (payld_len > buf_len) { | |||||
| device_printf(dev, | |||||
| "RX payload %dbytes exceeds buflen %dbytes. Truncate.\n", | |||||
| payld_len, buf_len); | |||||
| payld_len = buf_len; | |||||
| } | |||||
| /* Read response payload */ | |||||
| scmi_shmem_read(dev, SMT_SIZE_HEADER, buf, payld_len); | |||||
| return (0); | |||||
| } | } | ||||
| static device_method_t shmem_methods[] = { | static device_method_t shmem_methods[] = { | ||||
| DEVMETHOD(device_probe, shmem_probe), | DEVMETHOD(device_probe, shmem_probe), | ||||
| DEVMETHOD(device_attach, shmem_attach), | DEVMETHOD(device_attach, shmem_attach), | ||||
| DEVMETHOD(device_detach, shmem_detach), | DEVMETHOD(device_detach, shmem_detach), | ||||
| DEVMETHOD_END | DEVMETHOD_END | ||||
| }; | }; | ||||
| DEFINE_CLASS_1(shmem, shmem_driver, shmem_methods, sizeof(struct shmem_softc), | DEFINE_CLASS_1(shmem, shmem_driver, shmem_methods, sizeof(struct shmem_softc), | ||||
| simplebus_driver); | simplebus_driver); | ||||
| EARLY_DRIVER_MODULE(shmem, mmio_sram, shmem_driver, 0, 0, | EARLY_DRIVER_MODULE(shmem, mmio_sram, shmem_driver, 0, 0, | ||||
| BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE); | BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE); | ||||
| MODULE_VERSION(scmi, 1); | MODULE_VERSION(scmi, 1); | ||||