Add support for ARM System Control and Management Interface (SCMI) v3.1.

The SCMI specification describes a set of standard interfaces for power,
performance and system management.

SCMI is extensible and provides interfaces to access functions which are
often implemented in firmwares in the System Control Processor (SCP).

This implements Shared Memory-based transfer, which is one of the ways on
how messages are exchanged between agents and the platform.

This includes a driver for ARM Message Handling Unit (MHU) Doorbell, which
is a mechanism that the caller can use to alert the callee of the presence
of a message.

The support implements clock management interface. For instance this allows
us to control HDMI pixel clock on ARM Morello Board.

Tested on ARM Morello Board.

Obtained from: CheriBSD
Differential Revision:	https://reviews.freebsd.org/D37316
Reviewed by:	manu
Sponsored by:	UKRI
This commit is contained in:
Ruslan Bukin 2022-12-19 20:16:18 +00:00
parent 8183df7f00
commit 54b96380f5
13 changed files with 1735 additions and 0 deletions

View File

@ -11,5 +11,7 @@ device pl061 # Arm PL061 GPIO controller
# Serial (COM) ports
device pl011
device arm_doorbell # ARM Message Handling Unit (MHU)
options FDT
device acpi

View File

@ -107,3 +107,8 @@ device mmcsd # mmc/sd flash cards
# HID support
options HID_DEBUG # enable debug msgs
device hid # Generic HID support
# Firmware
device scmi # System Control Management Interface
device mmio_sram # Generic on-chip SRAM

View File

@ -210,6 +210,10 @@ dev/enetc/if_enetc.c optional enetc iflib pci fdt soc_nxp_ls
dev/etherswitch/felix/felix.c optional enetc etherswitch fdt felix pci soc_nxp_ls
dev/firmware/arm/scmi.c optional fdt scmi
dev/firmware/arm/scmi_clk.c optional fdt scmi
dev/firmware/arm/scmi_shmem.c optional fdt scmi
dev/gpio/pl061.c optional pl061 gpio
dev/gpio/pl061_acpi.c optional pl061 gpio acpi
dev/gpio/pl061_fdt.c optional pl061 gpio fdt
@ -311,6 +315,7 @@ dev/ipmi/ipmi_acpi.c optional ipmi acpi
dev/ipmi/ipmi_kcs.c optional ipmi
dev/ipmi/ipmi_smic.c optional ipmi
dev/mailbox/arm/arm_doorbell.c optional fdt arm_doorbell
dev/mbox/mbox_if.m optional soc_brcm_bcm2837
dev/mmc/host/dwmmc.c optional dwmmc fdt
@ -343,6 +348,9 @@ dev/sdhci/sdhci_xenon.c optional sdhci_xenon sdhci
dev/sdhci/sdhci_xenon_acpi.c optional sdhci_xenon sdhci acpi
dev/sdhci/sdhci_xenon_fdt.c optional sdhci_xenon sdhci fdt
dev/sram/mmio_sram.c optional fdt mmio_sram
dev/sram/mmio_sram_if.m optional fdt mmio_sram
dev/uart/uart_cpu_arm64.c optional uart
dev/uart/uart_dev_mu.c optional uart uart_mu fdt
dev/uart/uart_dev_pl011.c optional uart pl011

273
sys/dev/firmware/arm/scmi.c Normal file
View File

@ -0,0 +1,273 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2022 Ruslan Bukin <br@bsdpad.com>
*
* This work was supported by Innovate UK project 105694, "Digital Security
* by Design (DSbD) Technology Platform Prototype".
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/cpu.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/module.h>
#include <sys/mutex.h>
#include <dev/extres/clk/clk.h>
#include <dev/fdt/simplebus.h>
#include <dev/fdt/fdt_common.h>
#include <dev/ofw/ofw_bus_subr.h>
#include "dev/mailbox/arm/arm_doorbell.h"
#include "scmi.h"
#include "scmi_protocols.h"
struct scmi_softc {
struct simplebus_softc simplebus_sc;
device_t dev;
device_t tx_shmem;
struct arm_doorbell *db;
struct mtx mtx;
int req_done;
};
static device_t
scmi_get_shmem(struct scmi_softc *sc, int index)
{
phandle_t *shmems;
phandle_t node;
device_t dev;
size_t len;
node = ofw_bus_get_node(sc->dev);
if (node <= 0)
return (NULL);
len = OF_getencprop_alloc_multi(node, "shmem", sizeof(*shmems),
(void **)&shmems);
if (len <= 0) {
device_printf(sc->dev, "%s: Can't get shmem node.\n", __func__);
return (NULL);
}
if (index >= len) {
OF_prop_free(shmems);
return (NULL);
}
dev = OF_device_from_xref(shmems[index]);
if (dev == NULL)
device_printf(sc->dev, "%s: Can't get shmem device.\n",
__func__);
OF_prop_free(shmems);
return (dev);
}
static void
scmi_callback(void *arg)
{
struct scmi_softc *sc;
sc = arg;
dprintf("%s sc %p\n", __func__, sc);
SCMI_LOCK(sc);
sc->req_done = 1;
wakeup(sc);
SCMI_UNLOCK(sc);
}
static int
scmi_request_locked(struct scmi_softc *sc, struct scmi_req *req)
{
struct scmi_smt_header hdr;
int timeout;
bzero(&hdr, sizeof(struct scmi_smt_header));
SCMI_ASSERT_LOCKED(sc);
/* Read header */
scmi_shmem_read(sc->tx_shmem, 0, &hdr, SMT_HEADER_SIZE);
if ((hdr.channel_status & SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE) == 0)
return (1);
/* Update header */
hdr.channel_status &= ~SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE;
hdr.msg_header = req->protocol_id << SMT_HEADER_PROTOCOL_ID_S;
hdr.msg_header |= req->message_id << SMT_HEADER_MESSAGE_ID_S;
hdr.length = sizeof(hdr.msg_header) + req->in_size;
hdr.flags |= SCMI_SHMEM_FLAG_INTR_ENABLED;
/* Write header */
scmi_shmem_write(sc->tx_shmem, 0, &hdr, SMT_HEADER_SIZE);
/* Write request */
scmi_shmem_write(sc->tx_shmem, SMT_HEADER_SIZE, req->in_buf,
req->in_size);
sc->req_done = 0;
/* Interrupt SCP firmware. */
arm_doorbell_set(sc->db);
timeout = 200;
dprintf("%s: request\n", __func__);
do {
if (cold) {
if (arm_doorbell_get(sc->db))
break;
DELAY(10000);
} else {
msleep(sc, &sc->mtx, 0, "scmi", hz / 10);
if (sc->req_done)
break;
}
} while (timeout--);
if (timeout <= 0)
return (-1);
dprintf("%s: got reply, timeout %d\n", __func__, timeout);
/* Read header. */
scmi_shmem_read(sc->tx_shmem, 0, &hdr, SMT_HEADER_SIZE);
/* Read response */
scmi_shmem_read(sc->tx_shmem, SMT_HEADER_SIZE, req->out_buf,
req->out_size);
return (0);
}
int
scmi_request(device_t dev, struct scmi_req *req)
{
struct scmi_softc *sc;
int error;
sc = device_get_softc(dev);
SCMI_LOCK(sc);
error = scmi_request_locked(sc, req);
SCMI_UNLOCK(sc);
return (error);
}
static int
scmi_probe(device_t dev)
{
if (!ofw_bus_is_compatible(dev, "arm,scmi"))
return (ENXIO);
if (!ofw_bus_status_okay(dev))
return (ENXIO);
device_set_desc(dev, "ARM SCMI interface driver");
return (BUS_PROBE_DEFAULT);
}
static int
scmi_attach(device_t dev)
{
struct scmi_softc *sc;
phandle_t node;
int error;
sc = device_get_softc(dev);
sc->dev = dev;
node = ofw_bus_get_node(dev);
if (node == -1)
return (ENXIO);
sc->tx_shmem = scmi_get_shmem(sc, 0);
if (sc->tx_shmem == NULL) {
device_printf(dev, "TX shmem dev not found.\n");
return (ENXIO);
}
sc->db = arm_doorbell_ofw_get(sc->dev, "tx");
if (sc->db == NULL) {
device_printf(dev, "Doorbell device not found.\n");
return (ENXIO);
}
mtx_init(&sc->mtx, device_get_nameunit(dev), "SCMI", MTX_DEF);
arm_doorbell_set_handler(sc->db, scmi_callback, sc);
simplebus_init(dev, node);
/*
* Allow devices to identify.
*/
bus_generic_probe(dev);
/*
* Now walk the OFW tree and attach top-level devices.
*/
for (node = OF_child(node); node > 0; node = OF_peer(node))
simplebus_add_device(dev, node, 0, NULL, -1, NULL);
error = bus_generic_attach(dev);
return (error);
}
static int
scmi_detach(device_t dev)
{
return (0);
}
static device_method_t scmi_methods[] = {
DEVMETHOD(device_probe, scmi_probe),
DEVMETHOD(device_attach, scmi_attach),
DEVMETHOD(device_detach, scmi_detach),
DEVMETHOD_END
};
DEFINE_CLASS_1(scmi, scmi_driver, scmi_methods, sizeof(struct scmi_softc),
simplebus_driver);
DRIVER_MODULE(scmi, simplebus, scmi_driver, 0, 0);
MODULE_VERSION(scmi, 1);

View File

@ -0,0 +1,82 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2022 Ruslan Bukin <br@bsdpad.com>
*
* This work was supported by Innovate UK project 105694, "Digital Security
* by Design (DSbD) Technology Platform Prototype".
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#ifndef _ARM64_SCMI_SCMI_H_
#define _ARM64_SCMI_SCMI_H_
#define SCMI_LOCK(sc) mtx_lock(&(sc)->mtx)
#define SCMI_UNLOCK(sc) mtx_unlock(&(sc)->mtx)
#define SCMI_ASSERT_LOCKED(sc) mtx_assert(&(sc)->mtx, MA_OWNED)
#define dprintf(fmt, ...)
/* Shared Memory Transfer. */
struct scmi_smt_header {
uint32_t reserved;
uint32_t channel_status;
#define SCMI_SHMEM_CHAN_STAT_CHANNEL_ERROR (1 << 1)
#define SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE (1 << 0)
uint32_t reserved1[2];
uint32_t flags;
#define SCMI_SHMEM_FLAG_INTR_ENABLED (1 << 0)
uint32_t length;
uint32_t msg_header;
uint8_t msg_payload[0];
};
#define SMT_HEADER_SIZE sizeof(struct scmi_smt_header)
#define SMT_HEADER_TOKEN_S 18
#define SMT_HEADER_TOKEN_M (0x3fff << SMT_HEADER_TOKEN_S)
#define SMT_HEADER_PROTOCOL_ID_S 10
#define SMT_HEADER_PROTOCOL_ID_M (0xff << SMT_HEADER_PROTOCOL_ID_S)
#define SMT_HEADER_MESSAGE_TYPE_S 8
#define SMT_HEADER_MESSAGE_TYPE_M (0x3 << SMT_HEADER_MESSAGE_TYPE_S)
#define SMT_HEADER_MESSAGE_ID_S 0
#define SMT_HEADER_MESSAGE_ID_M (0xff << SMT_HEADER_MESSAGE_ID_S)
struct scmi_req {
int protocol_id;
int message_id;
const void *in_buf;
uint32_t in_size;
void *out_buf;
uint32_t out_size;
};
int scmi_request(device_t dev, struct scmi_req *req);
void scmi_shmem_read(device_t dev, bus_size_t offset, void *buf,
bus_size_t len);
void scmi_shmem_write(device_t dev, bus_size_t offset, const void *buf,
bus_size_t len);
#endif /* !_ARM64_SCMI_SCMI_H_ */

View File

@ -0,0 +1,434 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2022 Ruslan Bukin <br@bsdpad.com>
*
* This work was supported by Innovate UK project 105694, "Digital Security
* by Design (DSbD) Technology Platform Prototype".
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/cpu.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <dev/extres/clk/clk.h>
#include <dev/fdt/simplebus.h>
#include <dev/fdt/fdt_common.h>
#include <dev/ofw/ofw_bus_subr.h>
#include "scmi.h"
#include "scmi_protocols.h"
#include "scmi_clk.h"
struct scmi_clk_softc {
device_t dev;
device_t scmi;
struct clkdom *clkdom;
};
struct scmi_clknode_softc {
device_t dev;
int clock_id;
};
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;
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)
return (ENXIO);
*rate = out.rate_lsb | ((uint64_t)out.rate_msb << 32);
return (0);
}
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;
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.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);
if (out.status != 0)
return (ENXIO);
return (0);
}
static int
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;
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;
error = scmi_request(sc->scmi, &req);
if (error != 0)
return (error);
if (out.status != 0)
return (ENXIO);
return (0);
}
static int
scmi_clknode_init(struct clknode *clk, device_t dev)
{
clknode_init_parent_idx(clk, 0);
return (0);
}
static int
scmi_clknode_recalc_freq(struct clknode *clk, uint64_t *freq)
{
return (0);
}
static int
scmi_clknode_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout,
int flags, int *stop)
{
struct scmi_clknode_softc *clk_sc;
struct scmi_clk_softc *sc;
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;
return (0);
}
static clknode_method_t scmi_clknode_methods[] = {
/* Device interface */
CLKNODEMETHOD(clknode_init, scmi_clknode_init),
CLKNODEMETHOD(clknode_recalc_freq, scmi_clknode_recalc_freq),
CLKNODEMETHOD(clknode_set_freq, scmi_clknode_set_freq),
CLKNODEMETHOD_END
};
DEFINE_CLASS_1(scmi_clknode, scmi_clknode_class, scmi_clknode_methods,
sizeof(struct scmi_clknode_softc), clknode_class);
static int
scmi_clk_add_node(struct scmi_clk_softc *sc, int index, char *clock_name)
{
struct scmi_clknode_softc *clk_sc;
struct clknode_init_def def;
struct clknode *clk;
memset(&def, 0, sizeof(def));
def.id = index;
def.name = clock_name;
def.parent_names = NULL;
def.parent_cnt = 0;
clk = clknode_create(sc->clkdom, &scmi_clknode_class, &def);
if (clk == NULL) {
device_printf(sc->dev, "Cannot create clknode.\n");
return (ENXIO);
}
clk_sc = clknode_get_softc(clk);
clk_sc->dev = sc->dev;
clk_sc->clock_id = index;
if (clknode_register(sc->clkdom, clk) == NULL) {
device_printf(sc->dev, "Could not register clock '%s'.\n",
def.name);
return (ENXIO);
}
device_printf(sc->dev, "Clock '%s' registered.\n", def.name);
return (0);
}
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;
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)
return (ENXIO);
clock_name = malloc(sizeof(out.name), M_DEVBUF, M_WAITOK);
strncpy(clock_name, out.name, sizeof(out.name));
*result = clock_name;
return (0);
}
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;
char *clock_name;
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)
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));
}
error = scmi_clk_add_node(sc, index, clock_name);
return (error);
}
static int
scmi_clk_discover(struct scmi_clk_softc *sc)
{
struct scmi_clk_protocol_attrs_out out;
struct scmi_req req;
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)
return (ENXIO);
nclocks = (out.attributes & CLK_ATTRS_NCLOCKS_M) >>
CLK_ATTRS_NCLOCKS_S;
device_printf(sc->dev, "Found %d clocks.\n", nclocks);
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++;
}
}
if (failing == nclocks)
return (ENXIO);
return (0);
}
static int
scmi_clk_init(struct scmi_clk_softc *sc)
{
int error;
/* Create clock domain */
sc->clkdom = clkdom_create(sc->dev);
if (sc->clkdom == NULL)
return (ENXIO);
error = scmi_clk_discover(sc);
if (error) {
device_printf(sc->dev, "Could not discover clocks.\n");
return (ENXIO);
}
error = clkdom_finit(sc->clkdom);
if (error) {
device_printf(sc->dev, "Failed to init clock domain.\n");
return (ENXIO);
}
return (0);
}
static int
scmi_clk_probe(device_t dev)
{
phandle_t node;
uint32_t reg;
int error;
node = ofw_bus_get_node(dev);
error = OF_getencprop(node, "reg", &reg, sizeof(uint32_t));
if (error < 0)
return (ENXIO);
if (reg != SCMI_PROTOCOL_ID_CLOCK)
return (ENXIO);
device_set_desc(dev, "SCMI Clock Management Unit");
return (BUS_PROBE_DEFAULT);
}
static int
scmi_clk_attach(device_t dev)
{
struct scmi_clk_softc *sc;
phandle_t node;
sc = device_get_softc(dev);
sc->dev = dev;
sc->scmi = device_get_parent(dev);
node = ofw_bus_get_node(sc->dev);
OF_device_register_xref(OF_xref_from_node(node), sc->dev);
scmi_clk_init(sc);
return (0);
}
static int
scmi_clk_detach(device_t dev)
{
return (0);
}
static device_method_t scmi_clk_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, scmi_clk_probe),
DEVMETHOD(device_attach, scmi_clk_attach),
DEVMETHOD(device_detach, scmi_clk_detach),
DEVMETHOD_END
};
static driver_t scmi_clk_driver = {
"scmi_clk",
scmi_clk_methods,
sizeof(struct scmi_clk_softc),
};
EARLY_DRIVER_MODULE(scmi_clk, scmi, scmi_clk_driver, 0, 0,
BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
MODULE_VERSION(scmi_clk, 1);

View File

@ -0,0 +1,116 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2022 Ruslan Bukin <br@bsdpad.com>
*
* This work was supported by Innovate UK project 105694, "Digital Security
* by Design (DSbD) Technology Platform Prototype".
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#ifndef _ARM64_SCMI_SCMI_CLK_H_
#define _ARM64_SCMI_SCMI_CLK_H_
/*
* SCMI Clock Protocol
*/
struct scmi_clk_protocol_attrs_out {
int32_t status;
uint32_t attributes;
#define CLK_ATTRS_NCLOCKS_S 0
#define CLK_ATTRS_NCLOCKS_M (0xffff << CLK_ATTRS_NCLOCKS_S)
};
struct scmi_clk_attrs_in {
uint32_t clock_id;
};
struct scmi_clk_attrs_out {
int32_t status;
uint32_t attributes;
#define CLK_ATTRS_RATE_CHANGE_NOTIFY_SUPP (1 << 31)
#define CLK_ATTRS_RATE_REQ_CHANGE_NOTIFY_SUPP (1 << 30)
#define CLK_ATTRS_EXT_CLK_NAME (1 << 29)
#define CLK_ATTRS_ENABLED (1 << 0)
uint8_t clock_name[16]; /* only if attrs bit 29 unset */
uint32_t clock_enable_delay; /* worst case */
};
struct scmi_clk_name_get_in {
uint32_t clock_id;
};
struct scmi_clk_name_get_out {
int32_t status;
uint32_t flags;
uint8_t name[64];
};
enum scmi_clock_message_id {
SCMI_CLOCK_ATTRIBUTES = 0x3,
SCMI_CLOCK_RATE_SET = 0x5,
SCMI_CLOCK_RATE_GET = 0x6,
SCMI_CLOCK_CONFIG_SET = 0x7,
SCMI_CLOCK_NAME_GET = 0x8,
};
#define SCMI_CLK_RATE_ASYNC_NOTIFY (1 << 0)
#define SCMI_CLK_RATE_ASYNC_NORESP (1 << 0 | 1 << 1)
#define SCMI_CLK_RATE_ROUND_DOWN 0
#define SCMI_CLK_RATE_ROUND_UP (1 << 2)
#define SCMI_CLK_RATE_ROUND_CLOSEST (1 << 3)
struct scmi_clk_state_in {
uint32_t clock_id;
uint32_t attributes;
};
struct scmi_clk_state_out {
int32_t status;
};
struct scmi_clk_rate_get_in {
uint32_t clock_id;
};
struct scmi_clk_rate_get_out {
int32_t status;
uint32_t rate_lsb;
uint32_t rate_msb;
};
struct scmi_clk_rate_set_in {
uint32_t flags;
uint32_t clock_id;
uint32_t rate_lsb;
uint32_t rate_msb;
};
struct scmi_clk_rate_set_out {
int32_t status;
};
#endif /* !_ARM64_SCMI_SCMI_CLK_H_ */

View File

@ -0,0 +1,63 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2022 Ruslan Bukin <br@bsdpad.com>
*
* This work was supported by Innovate UK project 105694, "Digital Security
* by Design (DSbD) Technology Platform Prototype".
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#ifndef _ARM64_SCMI_SCMI_PROTOCOLS_H_
#define _ARM64_SCMI_SCMI_PROTOCOLS_H_
enum scmi_std_protocol {
SCMI_PROTOCOL_ID_BASE = 0x10,
SCMI_PROTOCOL_ID_POWER_DOMAIN = 0x11,
SCMI_PROTOCOL_ID_SYSTEM = 0x12,
SCMI_PROTOCOL_ID_PERF = 0x13,
SCMI_PROTOCOL_ID_CLOCK = 0x14,
SCMI_PROTOCOL_ID_SENSOR = 0x15,
SCMI_PROTOCOL_ID_RESET_DOMAIN = 0x16,
SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN = 0x17,
};
enum scmi_status_code {
SCMI_SUCCESS = 0,
SCMI_NOT_SUPPORTED = -1,
SCMI_INVALID_PARAMETERS = -2,
SCMI_DENIED = -3,
SCMI_NOT_FOUND = -4,
SCMI_OUT_OF_RANGE = -5,
SCMI_BUSY = -6,
SCMI_COMMS_ERROR = -7,
SCMI_GENERIC_ERROR = -8,
SCMI_HARDWARE_ERROR = -9,
SCMI_PROTOCOL_ERROR = -10,
};
#define SCMI_PROTOCOL_ATTRIBUTES 0x1
#endif /* !_ARM64_SCMI_SCMI_PROTOCOLS_H_ */

View File

@ -0,0 +1,146 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2022 Ruslan Bukin <br@bsdpad.com>
*
* This work was supported by Innovate UK project 105694, "Digital Security
* by Design (DSbD) Technology Platform Prototype".
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/cpu.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <dev/fdt/simplebus.h>
#include <dev/fdt/fdt_common.h>
#include <dev/ofw/ofw_bus_subr.h>
#include "mmio_sram_if.h"
#include "scmi.h"
struct shmem_softc {
device_t dev;
device_t parent;
int reg;
};
static int
shmem_probe(device_t dev)
{
if (!ofw_bus_is_compatible(dev, "arm,scmi-shmem"))
return (ENXIO);
if (!ofw_bus_status_okay(dev))
return (ENXIO);
device_set_desc(dev, "ARM SCMI Shared Memory driver");
return (BUS_PROBE_DEFAULT);
}
static int
shmem_attach(device_t dev)
{
struct shmem_softc *sc;
phandle_t node;
int reg;
sc = device_get_softc(dev);
sc->dev = dev;
sc->parent = device_get_parent(dev);
node = ofw_bus_get_node(dev);
if (node == -1)
return (ENXIO);
OF_getencprop(node, "reg", &reg, sizeof(reg));
dprintf("%s: reg %x\n", __func__, reg);
sc->reg = reg;
OF_device_register_xref(OF_xref_from_node(node), dev);
return (0);
}
static int
shmem_detach(device_t dev)
{
return (0);
}
void
scmi_shmem_read(device_t dev, bus_size_t offset, void *buf, bus_size_t len)
{
struct shmem_softc *sc;
uint8_t *addr;
int i;
sc = device_get_softc(dev);
addr = (uint8_t *)buf;
for (i = 0; i < len; i++)
addr[i] = MMIO_SRAM_READ_1(sc->parent, sc->reg + offset + i);
}
void
scmi_shmem_write(device_t dev, bus_size_t offset, const void *buf,
bus_size_t len)
{
struct shmem_softc *sc;
const uint8_t *addr;
int i;
sc = device_get_softc(dev);
addr = (const uint8_t *)buf;
for (i = 0; i < len; i++)
MMIO_SRAM_WRITE_1(sc->parent, sc->reg + offset + i, addr[i]);
}
static device_method_t shmem_methods[] = {
DEVMETHOD(device_probe, shmem_probe),
DEVMETHOD(device_attach, shmem_attach),
DEVMETHOD(device_detach, shmem_detach),
DEVMETHOD_END
};
DEFINE_CLASS_1(shmem, shmem_driver, shmem_methods, sizeof(struct shmem_softc),
simplebus_driver);
EARLY_DRIVER_MODULE(shmem, mmio_sram, shmem_driver, 0, 0,
BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
MODULE_VERSION(scmi, 1);

View File

@ -0,0 +1,347 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2022 Ruslan Bukin <br@bsdpad.com>
*
* This work was supported by Innovate UK project 105694, "Digital Security
* by Design (DSbD) Technology Platform Prototype".
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/rman.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <machine/bus.h>
#include <dev/fdt/simplebus.h>
#include <dev/fdt/fdt_common.h>
#include <dev/ofw/ofw_bus_subr.h>
#include "arm_doorbell.h"
#define MHU_CHAN_RX_LP 0x000 /* Low priority channel */
#define MHU_CHAN_RX_HP 0x020 /* High priority channel */
#define MHU_CHAN_RX_SEC 0x200 /* Secure channel */
#define MHU_INTR_STAT 0x00
#define MHU_INTR_SET 0x08
#define MHU_INTR_CLEAR 0x10
#define MHU_TX_REG_OFFSET 0x100
#define DOORBELL_N_CHANNELS 3
#define DOORBELL_N_DOORBELLS (DOORBELL_N_CHANNELS * 32)
struct arm_doorbell dbells[DOORBELL_N_DOORBELLS];
static struct resource_spec arm_doorbell_spec[] = {
{ SYS_RES_MEMORY, 0, RF_ACTIVE },
{ SYS_RES_IRQ, 0, RF_ACTIVE },
{ SYS_RES_IRQ, 1, RF_ACTIVE },
{ -1, 0 }
};
struct arm_doorbell_softc {
struct resource *res[3];
void *lp_intr_cookie;
void *hp_intr_cookie;
device_t dev;
};
static void
arm_doorbell_lp_intr(void *arg)
{
struct arm_doorbell_softc *sc;
struct arm_doorbell *db;
uint32_t reg;
int i;
sc = arg;
reg = bus_read_4(sc->res[0], MHU_CHAN_RX_LP + MHU_INTR_STAT);
for (i = 0; i < 32; i++) {
if (reg & (1 << i)) {
db = &dbells[i];
bus_write_4(sc->res[0], MHU_CHAN_RX_LP + MHU_INTR_CLEAR,
(1 << i));
if (db->func != NULL)
db->func(db->arg);
}
}
}
static void
arm_doorbell_hp_intr(void *arg)
{
struct arm_doorbell_softc *sc;
struct arm_doorbell *db;
uint32_t reg;
int i;
sc = arg;
reg = bus_read_4(sc->res[0], MHU_CHAN_RX_HP + MHU_INTR_STAT);
for (i = 0; i < 32; i++) {
if (reg & (1 << i)) {
db = &dbells[i];
bus_write_4(sc->res[0], MHU_CHAN_RX_HP + MHU_INTR_CLEAR,
(1 << i));
if (db->func != NULL)
db->func(db->arg);
}
}
}
static int
arm_doorbell_probe(device_t dev)
{
if (!ofw_bus_is_compatible(dev, "arm,mhu-doorbell"))
return (ENXIO);
if (!ofw_bus_status_okay(dev))
return (ENXIO);
device_set_desc(dev, "ARM MHU Doorbell");
return (BUS_PROBE_DEFAULT);
}
static int
arm_doorbell_attach(device_t dev)
{
struct arm_doorbell_softc *sc;
phandle_t node;
int error;
sc = device_get_softc(dev);
sc->dev = dev;
node = ofw_bus_get_node(dev);
if (node == -1)
return (ENXIO);
if (bus_alloc_resources(dev, arm_doorbell_spec, sc->res) != 0) {
device_printf(dev, "Can't allocate resources for device.\n");
return (ENXIO);
}
/* Setup interrupt handlers. */
error = bus_setup_intr(dev, sc->res[1], INTR_TYPE_MISC | INTR_MPSAFE,
NULL, arm_doorbell_lp_intr, sc, &sc->lp_intr_cookie);
if (error != 0) {
device_printf(dev, "Can't setup LP interrupt handler.\n");
bus_release_resources(dev, arm_doorbell_spec, sc->res);
return (ENXIO);
}
error = bus_setup_intr(dev, sc->res[2], INTR_TYPE_MISC | INTR_MPSAFE,
NULL, arm_doorbell_hp_intr, sc, &sc->hp_intr_cookie);
if (error != 0) {
device_printf(dev, "Can't setup HP interrupt handler.\n");
bus_release_resources(dev, arm_doorbell_spec, sc->res);
return (ENXIO);
}
OF_device_register_xref(OF_xref_from_node(node), dev);
return (0);
}
static int
arm_doorbell_detach(device_t dev)
{
return (EBUSY);
}
struct arm_doorbell *
arm_doorbell_ofw_get(device_t dev, const char *name)
{
phandle_t node, parent;
struct arm_doorbell *db;
device_t db_dev;
pcell_t *cells;
int nmboxes;
int ncells;
int idx;
int db_id;
int error;
int chan;
node = ofw_bus_get_node(dev);
error = ofw_bus_parse_xref_list_get_length(node, "mboxes",
"#mbox-cells", &nmboxes);
if (error) {
device_printf(dev, "%s can't get mboxes list.\n", __func__);
return (NULL);
}
if (nmboxes == 0) {
device_printf(dev, "%s mbox list is empty.\n", __func__);
return (NULL);
}
error = ofw_bus_find_string_index(node, "mbox-names", name, &idx);
if (error != 0) {
device_printf(dev, "%s can't find string index.\n",
__func__);
return (NULL);
}
error = ofw_bus_parse_xref_list_alloc(node, "mboxes", "#mbox-cells",
idx, &parent, &ncells, &cells);
if (error != 0) {
device_printf(dev, "%s can't get mbox device xref\n",
__func__);
return (NULL);
}
if (ncells != 2) {
device_printf(dev, "Unexpected data size.\n");
OF_prop_free(cells);
return (NULL);
}
db_dev = OF_device_from_xref(parent);
if (db_dev == NULL) {
device_printf(dev, "%s: Can't get arm_doorbell device\n",
__func__);
OF_prop_free(cells);
return (NULL);
}
chan = cells[0];
if (chan >= DOORBELL_N_CHANNELS) {
device_printf(dev, "Unexpected channel number.\n");
OF_prop_free(cells);
return (NULL);
}
db_id = cells[1];
if (db_id >= 32) {
device_printf(dev, "Unexpected channel bit.\n");
OF_prop_free(cells);
return (NULL);
}
db = &dbells[chan * db_id];
db->dev = dev;
db->db_dev = db_dev;
db->chan = chan;
db->db = db_id;
OF_prop_free(cells);
return (db);
}
void
arm_doorbell_set(struct arm_doorbell *db)
{
struct arm_doorbell_softc *sc;
uint32_t offset;
sc = device_get_softc(db->db_dev);
switch (db->chan) {
case 0:
offset = MHU_CHAN_RX_LP;
break;
case 1:
offset = MHU_CHAN_RX_HP;
break;
case 2:
offset = MHU_CHAN_RX_SEC;
break;
default:
panic("not reached");
};
offset |= MHU_TX_REG_OFFSET;
bus_write_4(sc->res[0], offset + MHU_INTR_SET, (1 << db->db));
}
int
arm_doorbell_get(struct arm_doorbell *db)
{
struct arm_doorbell_softc *sc;
uint32_t offset;
uint32_t reg;
sc = device_get_softc(db->db_dev);
switch (db->chan) {
case 0:
offset = MHU_CHAN_RX_LP;
break;
case 1:
offset = MHU_CHAN_RX_HP;
break;
case 2:
offset = MHU_CHAN_RX_SEC;
break;
default:
panic("not reached");
};
reg = bus_read_4(sc->res[0], offset + MHU_INTR_STAT);
if (reg & (1 << db->db)) {
bus_write_4(sc->res[0], offset + MHU_INTR_CLEAR,
(1 << db->db));
return (1);
}
return (0);
}
void
arm_doorbell_set_handler(struct arm_doorbell *db, void (*func)(void *),
void *arg)
{
db->func = func;
db->arg = arg;
}
static device_method_t arm_doorbell_methods[] = {
DEVMETHOD(device_probe, arm_doorbell_probe),
DEVMETHOD(device_attach, arm_doorbell_attach),
DEVMETHOD(device_detach, arm_doorbell_detach),
DEVMETHOD_END
};
DEFINE_CLASS_1(arm_doorbell, arm_doorbell_driver, arm_doorbell_methods,
sizeof(struct arm_doorbell_softc), simplebus_driver);
EARLY_DRIVER_MODULE(arm_doorbell, simplebus, arm_doorbell_driver, 0, 0,
BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
MODULE_VERSION(arm_doorbell, 1);

View File

@ -0,0 +1,51 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2022 Ruslan Bukin <br@bsdpad.com>
*
* This work was supported by Innovate UK project 105694, "Digital Security
* by Design (DSbD) Technology Platform Prototype".
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#ifndef _ARM64_SCMI_ARM_DOORBELL_H_
#define _ARM64_SCMI_ARM_DOORBELL_H_
struct arm_doorbell {
device_t dev;
device_t db_dev;
int chan;
int db;
void (*func)(void *);
void *arg;
};
void arm_doorbell_set(struct arm_doorbell *db);
int arm_doorbell_get(struct arm_doorbell *db);
struct arm_doorbell * arm_doorbell_ofw_get(device_t dev, const char *name);
void arm_doorbell_set_handler(struct arm_doorbell *db, void (*func)(void *),
void *arg);
#endif /* !_ARM64_SCMI_ARM_DOORBELL_H_ */

164
sys/dev/sram/mmio_sram.c Normal file
View File

@ -0,0 +1,164 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2022 Ruslan Bukin <br@bsdpad.com>
*
* This work was supported by Innovate UK project 105694, "Digital Security
* by Design (DSbD) Technology Platform Prototype".
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/rman.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <machine/bus.h>
#include <dev/fdt/simplebus.h>
#include <dev/fdt/fdt_common.h>
#include <dev/ofw/ofw_bus_subr.h>
#include "mmio_sram_if.h"
#define dprintf(fmt, ...)
static struct resource_spec mmio_sram_spec[] = {
{ SYS_RES_MEMORY, 0, RF_ACTIVE },
{ -1, 0 }
};
struct mmio_sram_softc {
struct simplebus_softc simplebus_sc;
struct resource *res[1];
device_t dev;
};
static int
mmio_sram_probe(device_t dev)
{
if (!ofw_bus_is_compatible(dev, "mmio-sram"))
return (ENXIO);
if (!ofw_bus_status_okay(dev))
return (ENXIO);
device_set_desc(dev, "MMIO SRAM");
return (BUS_PROBE_DEFAULT);
}
static int
mmio_sram_attach(device_t dev)
{
struct mmio_sram_softc *sc;
phandle_t node;
sc = device_get_softc(dev);
sc->dev = dev;
if (bus_alloc_resources(dev, mmio_sram_spec, sc->res) != 0) {
device_printf(dev, "Can't allocate resources for device.\n");
return (ENXIO);
}
node = ofw_bus_get_node(dev);
if (node == -1)
return (ENXIO);
simplebus_init(dev, node);
/*
* Allow devices to identify.
*/
bus_generic_probe(dev);
/*
* Now walk the OFW tree and attach top-level devices.
*/
for (node = OF_child(node); node > 0; node = OF_peer(node))
simplebus_add_device(dev, node, 0, NULL, -1, NULL);
return (bus_generic_attach(dev));
}
static int
mmio_sram_detach(device_t dev)
{
struct mmio_sram_softc *sc;
sc = device_get_softc(dev);
bus_release_resources(dev, mmio_sram_spec, sc->res);
return (0);
}
static uint8_t
mmio_sram_read_1(device_t dev, bus_size_t offset)
{
struct mmio_sram_softc *sc;
sc = device_get_softc(dev);
dprintf("%s: reading from %lx\n", __func__, offset);
return (bus_read_1(sc->res[0], offset));
}
static void
mmio_sram_write_1(device_t dev, bus_size_t offset, uint8_t val)
{
struct mmio_sram_softc *sc;
sc = device_get_softc(dev);
dprintf("%s: writing to %lx val %x\n", __func__, offset, val);
bus_write_1(sc->res[0], offset, val);
}
static device_method_t mmio_sram_methods[] = {
/* Device Interface */
DEVMETHOD(device_probe, mmio_sram_probe),
DEVMETHOD(device_attach, mmio_sram_attach),
DEVMETHOD(device_detach, mmio_sram_detach),
/* MMIO interface */
DEVMETHOD(mmio_sram_read_1, mmio_sram_read_1),
DEVMETHOD(mmio_sram_write_1, mmio_sram_write_1),
DEVMETHOD_END
};
DEFINE_CLASS_1(mmio_sram, mmio_sram_driver, mmio_sram_methods,
sizeof(struct mmio_sram_softc), simplebus_driver);
EARLY_DRIVER_MODULE(mmio_sram, simplebus, mmio_sram_driver, 0, 0,
BUS_PASS_SUPPORTDEV + BUS_PASS_ORDER_MIDDLE);
MODULE_VERSION(mmio_sram, 1);

View File

@ -0,0 +1,44 @@
#-
# SPDX-License-Identifier: BSD-2-Clause
#
# Copyright (c) 2022 Ruslan Bukin <br@bsdpad.com>
#
# This work was supported by Innovate UK project 105694, "Digital Security by
# Design (DSbD) Technology Platform Prototype".
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# $FreeBSD$
#
#include <machine/bus.h>
INTERFACE mmio_sram;
METHOD uint8_t read_1 {
device_t dev;
bus_size_t offset;
};
METHOD void write_1 {
device_t dev;
bus_size_t offset;
uint8_t value;
};