mirror of
https://github.com/torvalds/linux.git
synced 2026-05-21 13:27:57 +02:00
Arm SCMI updates for v6.3
The main addition is a unified userspace interface for SCMI irrespective
of the underlying transport and along with some changed to refactor the
SCMI stack probing sequence.
1. SCMI unified userspace interface
This is to have a unified way of testing an SCMI platform firmware
implementation for compliance, fuzzing etc., from the perspective of
the non-secure OSPM irrespective of the underlying transport supporting
SCMI. It is just for testing/development and not a feature intended fo
use in production.
Currently an SCMI Compliance Suite[1] can only work by injecting SCMI
messages using the mailbox test driver only which makes it transport
specific and can't be used with any other transport like virtio,
smc/hvc, optee, etc. Also the shared memory can be transport specific
and it is better to even abstract/hide those details while providing
the userspace access. So in order to scale with any transport, we need
a unified interface for the same.
In order to achieve that, SCMI "raw mode support" is being added through
debugfs which is more configurable as well. A userspace application
can inject bare SCMI binary messages into the SCMI core stack; such
messages will be routed by the SCMI regular kernel stack to the backend
platform firmware using the configured transport transparently. This
eliminates the to know about the specific underlying transport
internals that will be taken care of by the SCMI core stack itself.
Further no additional changes needed in the device tree like in the
mailbox-test driver.
[1] https://gitlab.arm.com/tests/scmi-tests
2. Refactoring of the SCMI stack probing sequence
On some platforms, SCMI transport can be provide by OPTEE/TEE which
introduces certain dependency in the probe ordering. In order to address
the same, the SCMI bus is split into its own module which continues to
be initialized at subsys_initcall, while the SCMI core stack, including
its various transport backends (like optee, mailbox, virtio, smc), is
now moved into a separate module at module_init level.
This allows the other possibly dependent subsystems to register and/or
access SCMI bus well before the core SCMI stack and its dependent
transport backends.
-----BEGIN PGP SIGNATURE-----
iQIzBAABCAAdFiEEunHlEgbzHrJD3ZPhAEG6vDF+4pgFAmPKvdkACgkQAEG6vDF+
4pgB0g//eU5S0aTgt8XlwDmdjeu+mNrj68QHKINq9CS7PmBs37So0IdLJ+CpqJlo
VSmk2kI5oLWz/u3N92QQY9RXM4hvO95kiPKuyO8NsoPWrfjBZH3rKcgEpRquZjrt
TdBUPd2aqoKhFqkUzxs5lNEZOV/R6mm0q+i9dD4RIRKP9Tjrlm3jYDSMFnW3/QMJ
OR3Ub0e/4Lj3QyNUxrUqwpdjTiAqXimCW7LWZ2fwY5kPxcL4wedAfQS3zGa5m8Wk
htqRTXmYtSVKAZ/oFUPDOHuZUNqn0ZdNI7guEPgzo90+pJs0yQUQf/wtc+X9quXZ
/FUGaVSTzlvcl1MPJPTPQ9d7dJH8lR0+nxzovkBSoMX/tNByuVtBUpNiclu8seob
CqbywRtASkd0g6dKHHEIylwj0FpRSYBLJEcE6jXhxfvXt+sCDZbDSUpWaGGZnNqO
oj8FhEmRk/t/d+ZEkn6MlRgy5uiJSv4GstNQ5V/ZSz3vhp1u2Sl7y8xAcuVsqXyH
Dok6iM9GoSdskCdSICk5iA2ESC5s+1IiDd2PnSwWz+yj9HbmWKwU0nKgRRWvDUZh
jJYWAvcwuh3SQ/sN/FNTVsxQt/x5V3L/K7oe2o983l0Lq8k9WB0mEBAyRidxAlVj
TpfXbe9CDqJtoF85Bslb7eS++iGADkVvmWAqjqUQddFQR++Dj2w=
=yLSu
-----END PGP SIGNATURE-----
gpgsig -----BEGIN PGP SIGNATURE-----
iQIzBAABCgAdFiEEo6/YBQwIrVS28WGKmmx57+YAGNkFAmPQQ0QACgkQmmx57+YA
GNlYsRAAr/qGRRSiOtTYZi0yPmSuGCYb4xORbJHS00gD3aRzrehtsf//av4WyeyM
cGpeR/2OIa66/M7wIjKM7lHXBdzFxLgwAM9DkexVwY1i7w6ExDZFClX7UhlgmwiM
LbsApqHYW/DhAnOBL7SXPQkDIcTifURvOZCOJ0NJg5HOn+DeRP5UPEE5Xbqt0+yc
JUnKC2eqN15G/BWJCWtmfZVXwMKtWB8ScU2HJeoEjjH7d31XvWvw5z0upyhlEQvb
lqhFgKftEY+boEfbz86AoZoBY8EWFdqB8RvcNOmReU8hyequjnrK/ykQq9EAAMmo
LbxNkW72zN727ji3O7onX8zWjqYLKjV3FC/cuNah//USQa8nPvq4atXf+D7my0fC
Rm9ZF3ztHQjTgMVlmm/uAU3c4BxEcwHMnppEoYvxa23ErSt+hp2aJvfDfseMAYFW
2iie4KAR03WeHnspYioa88PHj83SeT9AsHxf+PCdXkXgzl74HDVrt/8gpLQMmu4Q
BTkwTF3QvEry5c6uF7yI/oOuQq06Y03miN5GEQKtqTIIpn3g/tJs0GJot0nUPZ0f
xJJ+mlOcoc76zwaDARcGJO0yzX9qJt4I+/HS39BLvMt2EfbZ3IXlSsQu7esd5p2V
6xokmygD5rZY92CMd3SPpJ4xRBHUN6PIFb1GUyJmg9xoghDwjXs=
=jvm3
-----END PGP SIGNATURE-----
Merge tag 'scmi-updates-6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/sudeep.holla/linux into soc/drivers
Arm SCMI updates for v6.3
The main addition is a unified userspace interface for SCMI irrespective
of the underlying transport and along with some changed to refactor the
SCMI stack probing sequence.
1. SCMI unified userspace interface
This is to have a unified way of testing an SCMI platform firmware
implementation for compliance, fuzzing etc., from the perspective of
the non-secure OSPM irrespective of the underlying transport supporting
SCMI. It is just for testing/development and not a feature intended fo
use in production.
Currently an SCMI Compliance Suite[1] can only work by injecting SCMI
messages using the mailbox test driver only which makes it transport
specific and can't be used with any other transport like virtio,
smc/hvc, optee, etc. Also the shared memory can be transport specific
and it is better to even abstract/hide those details while providing
the userspace access. So in order to scale with any transport, we need
a unified interface for the same.
In order to achieve that, SCMI "raw mode support" is being added through
debugfs which is more configurable as well. A userspace application
can inject bare SCMI binary messages into the SCMI core stack; such
messages will be routed by the SCMI regular kernel stack to the backend
platform firmware using the configured transport transparently. This
eliminates the to know about the specific underlying transport
internals that will be taken care of by the SCMI core stack itself.
Further no additional changes needed in the device tree like in the
mailbox-test driver.
[1] https://gitlab.arm.com/tests/scmi-tests
2. Refactoring of the SCMI stack probing sequence
On some platforms, SCMI transport can be provide by OPTEE/TEE which
introduces certain dependency in the probe ordering. In order to address
the same, the SCMI bus is split into its own module which continues to
be initialized at subsys_initcall, while the SCMI core stack, including
its various transport backends (like optee, mailbox, virtio, smc), is
now moved into a separate module at module_init level.
This allows the other possibly dependent subsystems to register and/or
access SCMI bus well before the core SCMI stack and its dependent
transport backends.
* tag 'scmi-updates-6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/sudeep.holla/linux: (31 commits)
firmware: arm_scmi: Clarify raw per-channel ABI documentation
firmware: arm_scmi: Add per-channel raw injection support
firmware: arm_scmi: Add the raw mode co-existence support
firmware: arm_scmi: Call raw mode hooks from the core stack
firmware: arm_scmi: Reject SCMI drivers when configured in raw mode
firmware: arm_scmi: Add debugfs ABI documentation for raw mode
firmware: arm_scmi: Add core raw transmission support
firmware: arm_scmi: Add debugfs ABI documentation for common entries
firmware: arm_scmi: Populate a common SCMI debugfs root
debugfs: Export debugfs_create_str symbol
include: trace: Add platform and channel instance references
firmware: arm_scmi: Add internal platform/channel identifiers
firmware: arm_scmi: Move errors defs and code to common.h
firmware: arm_scmi: Add xfer helpers to provide raw access
firmware: arm_scmi: Add flags field to xfer
firmware: arm_scmi: Refactor scmi_wait_for_message_response
firmware: arm_scmi: Refactor polling helpers
firmware: arm_scmi: Refactor xfer in-flight registration routines
firmware: arm_scmi: Split bus and driver into distinct modules
firmware: arm_scmi: Introduce a new lifecycle for protocol devices
...
Link: https://lore.kernel.org/r/20230120162152.1438456-1-sudeep.holla@arm.com
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
This commit is contained in:
commit
f938b29d27
70
Documentation/ABI/testing/debugfs-scmi
Normal file
70
Documentation/ABI/testing/debugfs-scmi
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
What: /sys/kernel/debug/scmi/<n>/instance_name
|
||||
Date: March 2023
|
||||
KernelVersion: 6.3
|
||||
Contact: cristian.marussi@arm.com
|
||||
Description: The name of the underlying SCMI instance <n> described by
|
||||
all the debugfs accessors rooted at /sys/kernel/debug/scmi/<n>,
|
||||
expressed as the full name of the top DT SCMI node under which
|
||||
this SCMI instance is rooted.
|
||||
Users: Debugging, any userspace test suite
|
||||
|
||||
What: /sys/kernel/debug/scmi/<n>/atomic_threshold_us
|
||||
Date: March 2023
|
||||
KernelVersion: 6.3
|
||||
Contact: cristian.marussi@arm.com
|
||||
Description: An optional time value, expressed in microseconds, representing,
|
||||
on this SCMI instance <n>, the threshold above which any SCMI
|
||||
command, advertised to have an higher-than-threshold execution
|
||||
latency, should not be considered for atomic mode of operation,
|
||||
even if requested.
|
||||
Users: Debugging, any userspace test suite
|
||||
|
||||
What: /sys/kernel/debug/scmi/<n>/transport/type
|
||||
Date: March 2023
|
||||
KernelVersion: 6.3
|
||||
Contact: cristian.marussi@arm.com
|
||||
Description: A string representing the type of transport configured for this
|
||||
SCMI instance <n>.
|
||||
Users: Debugging, any userspace test suite
|
||||
|
||||
What: /sys/kernel/debug/scmi/<n>/transport/is_atomic
|
||||
Date: March 2023
|
||||
KernelVersion: 6.3
|
||||
Contact: cristian.marussi@arm.com
|
||||
Description: A boolean stating if the transport configured on the underlying
|
||||
SCMI instance <n> is capable of atomic mode of operation.
|
||||
Users: Debugging, any userspace test suite
|
||||
|
||||
What: /sys/kernel/debug/scmi/<n>/transport/max_rx_timeout_ms
|
||||
Date: March 2023
|
||||
KernelVersion: 6.3
|
||||
Contact: cristian.marussi@arm.com
|
||||
Description: Timeout in milliseconds allowed for SCMI synchronous replies
|
||||
for the currently configured SCMI transport for instance <n>.
|
||||
Users: Debugging, any userspace test suite
|
||||
|
||||
What: /sys/kernel/debug/scmi/<n>/transport/max_msg_size
|
||||
Date: March 2023
|
||||
KernelVersion: 6.3
|
||||
Contact: cristian.marussi@arm.com
|
||||
Description: Max message size of allowed SCMI messages for the currently
|
||||
configured SCMI transport for instance <n>.
|
||||
Users: Debugging, any userspace test suite
|
||||
|
||||
What: /sys/kernel/debug/scmi/<n>/transport/tx_max_msg
|
||||
Date: March 2023
|
||||
KernelVersion: 6.3
|
||||
Contact: cristian.marussi@arm.com
|
||||
Description: Max number of concurrently allowed in-flight SCMI messages for
|
||||
the currently configured SCMI transport for instance <n> on the
|
||||
TX channels.
|
||||
Users: Debugging, any userspace test suite
|
||||
|
||||
What: /sys/kernel/debug/scmi/<n>/transport/rx_max_msg
|
||||
Date: March 2023
|
||||
KernelVersion: 6.3
|
||||
Contact: cristian.marussi@arm.com
|
||||
Description: Max number of concurrently allowed in-flight SCMI messages for
|
||||
the currently configured SCMI transport for instance <n> on the
|
||||
RX channels.
|
||||
Users: Debugging, any userspace test suite
|
||||
117
Documentation/ABI/testing/debugfs-scmi-raw
Normal file
117
Documentation/ABI/testing/debugfs-scmi-raw
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
What: /sys/kernel/debug/scmi/<n>/raw/message
|
||||
Date: March 2023
|
||||
KernelVersion: 6.3
|
||||
Contact: cristian.marussi@arm.com
|
||||
Description: SCMI Raw synchronous message injection/snooping facility; write
|
||||
a complete SCMI synchronous command message (header included)
|
||||
in little-endian binary format to have it sent to the configured
|
||||
backend SCMI server for instance <n>.
|
||||
Any subsequently received response can be read from this same
|
||||
entry if it arrived within the configured timeout.
|
||||
Each write to the entry causes one command request to be built
|
||||
and sent while the replies are read back one message at time
|
||||
(receiving an EOF at each message boundary).
|
||||
Users: Debugging, any userspace test suite
|
||||
|
||||
What: /sys/kernel/debug/scmi/<n>/raw/message_async
|
||||
Date: March 2023
|
||||
KernelVersion: 6.3
|
||||
Contact: cristian.marussi@arm.com
|
||||
Description: SCMI Raw asynchronous message injection/snooping facility; write
|
||||
a complete SCMI asynchronous command message (header included)
|
||||
in little-endian binary format to have it sent to the configured
|
||||
backend SCMI server for instance <n>.
|
||||
Any subsequently received response can be read from this same
|
||||
entry if it arrived within the configured timeout.
|
||||
Any additional delayed response received afterwards can be read
|
||||
from this same entry too if it arrived within the configured
|
||||
timeout.
|
||||
Each write to the entry causes one command request to be built
|
||||
and sent while the replies are read back one message at time
|
||||
(receiving an EOF at each message boundary).
|
||||
Users: Debugging, any userspace test suite
|
||||
|
||||
What: /sys/kernel/debug/scmi/<n>/raw/errors
|
||||
Date: March 2023
|
||||
KernelVersion: 6.3
|
||||
Contact: cristian.marussi@arm.com
|
||||
Description: SCMI Raw message errors facility; any kind of timed-out or
|
||||
generally unexpectedly received SCMI message, for instance <n>,
|
||||
can be read from this entry.
|
||||
Each read gives back one message at time (receiving an EOF at
|
||||
each message boundary).
|
||||
Users: Debugging, any userspace test suite
|
||||
|
||||
What: /sys/kernel/debug/scmi/<n>/raw/notification
|
||||
Date: March 2023
|
||||
KernelVersion: 6.3
|
||||
Contact: cristian.marussi@arm.com
|
||||
Description: SCMI Raw notification snooping facility; any notification
|
||||
emitted by the backend SCMI server, for instance <n>, can be
|
||||
read from this entry.
|
||||
Each read gives back one message at time (receiving an EOF at
|
||||
each message boundary).
|
||||
Users: Debugging, any userspace test suite
|
||||
|
||||
What: /sys/kernel/debug/scmi/<n>/raw/reset
|
||||
Date: March 2023
|
||||
KernelVersion: 6.3
|
||||
Contact: cristian.marussi@arm.com
|
||||
Description: SCMI Raw stack reset facility; writing a value to this entry
|
||||
causes the internal queues of any kind of received message,
|
||||
still pending to be read out for instance <n>, to be immediately
|
||||
flushed.
|
||||
Can be used to reset and clean the SCMI Raw stack between to
|
||||
different test-run.
|
||||
Users: Debugging, any userspace test suite
|
||||
|
||||
What: /sys/kernel/debug/scmi/<n>/raw/channels/<m>/message
|
||||
Date: March 2023
|
||||
KernelVersion: 6.3
|
||||
Contact: cristian.marussi@arm.com
|
||||
Description: SCMI Raw synchronous message injection/snooping facility; write
|
||||
a complete SCMI synchronous command message (header included)
|
||||
in little-endian binary format to have it sent to the configured
|
||||
backend SCMI server for instance <n> through the <m> transport
|
||||
channel.
|
||||
Any subsequently received response can be read from this same
|
||||
entry if it arrived on channel <m> within the configured
|
||||
timeout.
|
||||
Each write to the entry causes one command request to be built
|
||||
and sent while the replies are read back one message at time
|
||||
(receiving an EOF at each message boundary).
|
||||
Channel identifier <m> matches the SCMI protocol number which
|
||||
has been associated with this transport channel in the DT
|
||||
description, with base protocol number 0x10 being the default
|
||||
channel for this instance.
|
||||
Note that these per-channel entries rooted at <..>/channels
|
||||
exist only if the transport is configured to have more than
|
||||
one default channel.
|
||||
Users: Debugging, any userspace test suite
|
||||
|
||||
What: /sys/kernel/debug/scmi/<n>/raw/channels/<m>/message_async
|
||||
Date: March 2023
|
||||
KernelVersion: 6.3
|
||||
Contact: cristian.marussi@arm.com
|
||||
Description: SCMI Raw asynchronous message injection/snooping facility; write
|
||||
a complete SCMI asynchronous command message (header included)
|
||||
in little-endian binary format to have it sent to the configured
|
||||
backend SCMI server for instance <n> through the <m> transport
|
||||
channel.
|
||||
Any subsequently received response can be read from this same
|
||||
entry if it arrived on channel <m> within the configured
|
||||
timeout.
|
||||
Any additional delayed response received afterwards can be read
|
||||
from this same entry too if it arrived within the configured
|
||||
timeout.
|
||||
Each write to the entry causes one command request to be built
|
||||
and sent while the replies are read back one message at time
|
||||
(receiving an EOF at each message boundary).
|
||||
Channel identifier <m> matches the SCMI protocol number which
|
||||
has been associated with this transport channel in the DT
|
||||
description, with base protocol number 0x10 being the default
|
||||
channel for this instance.
|
||||
Note that these per-channel entries rooted at <..>/channels
|
||||
exist only if the transport is configured to have more than
|
||||
one default channel.
|
||||
Users: Debugging, any userspace test suite
|
||||
|
|
@ -23,6 +23,38 @@ config ARM_SCMI_PROTOCOL
|
|||
|
||||
if ARM_SCMI_PROTOCOL
|
||||
|
||||
config ARM_SCMI_NEED_DEBUGFS
|
||||
bool
|
||||
help
|
||||
This declares whether at least one SCMI facility is configured
|
||||
which needs debugfs support. When selected causess the creation
|
||||
of a common SCMI debugfs root directory.
|
||||
|
||||
config ARM_SCMI_RAW_MODE_SUPPORT
|
||||
bool "Enable support for SCMI Raw transmission mode"
|
||||
depends on DEBUG_FS
|
||||
select ARM_SCMI_NEED_DEBUGFS
|
||||
help
|
||||
Enable support for SCMI Raw transmission mode.
|
||||
|
||||
If enabled allows the direct injection and snooping of SCMI bare
|
||||
messages through a dedicated debugfs interface.
|
||||
It is meant to be used by SCMI compliance/testing suites.
|
||||
|
||||
When enabled regular SCMI drivers interactions are inhibited in
|
||||
order to avoid unexpected interactions with the SCMI Raw message
|
||||
flow. If unsure say N.
|
||||
|
||||
config ARM_SCMI_RAW_MODE_SUPPORT_COEX
|
||||
bool "Allow SCMI Raw mode coexistence with normal SCMI stack"
|
||||
depends on ARM_SCMI_RAW_MODE_SUPPORT
|
||||
help
|
||||
Allow SCMI Raw transmission mode to coexist with normal SCMI stack.
|
||||
|
||||
This will allow regular SCMI drivers to register with the core and
|
||||
operate normally, thing which could make an SCMI test suite using the
|
||||
SCMI Raw mode support unreliable. If unsure, say N.
|
||||
|
||||
config ARM_SCMI_HAVE_TRANSPORT
|
||||
bool
|
||||
help
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
scmi-bus-y = bus.o
|
||||
scmi-core-objs := $(scmi-bus-y)
|
||||
|
||||
scmi-driver-y = driver.o notify.o
|
||||
scmi-driver-$(CONFIG_ARM_SCMI_RAW_MODE_SUPPORT) += raw_mode.o
|
||||
scmi-transport-$(CONFIG_ARM_SCMI_HAVE_SHMEM) = shmem.o
|
||||
scmi-transport-$(CONFIG_ARM_SCMI_TRANSPORT_MAILBOX) += mailbox.o
|
||||
scmi-transport-$(CONFIG_ARM_SCMI_TRANSPORT_SMC) += smc.o
|
||||
|
|
@ -8,9 +11,11 @@ scmi-transport-$(CONFIG_ARM_SCMI_HAVE_MSG) += msg.o
|
|||
scmi-transport-$(CONFIG_ARM_SCMI_TRANSPORT_VIRTIO) += virtio.o
|
||||
scmi-transport-$(CONFIG_ARM_SCMI_TRANSPORT_OPTEE) += optee.o
|
||||
scmi-protocols-y = base.o clock.o perf.o power.o reset.o sensors.o system.o voltage.o powercap.o
|
||||
scmi-module-objs := $(scmi-bus-y) $(scmi-driver-y) $(scmi-protocols-y) \
|
||||
$(scmi-transport-y)
|
||||
scmi-module-objs := $(scmi-driver-y) $(scmi-protocols-y) $(scmi-transport-y)
|
||||
|
||||
obj-$(CONFIG_ARM_SCMI_PROTOCOL) += scmi-core.o
|
||||
obj-$(CONFIG_ARM_SCMI_PROTOCOL) += scmi-module.o
|
||||
|
||||
obj-$(CONFIG_ARM_SCMI_POWER_DOMAIN) += scmi_pm_domain.o
|
||||
obj-$(CONFIG_ARM_SCMI_POWER_CONTROL) += scmi_power_control.o
|
||||
|
||||
|
|
|
|||
|
|
@ -7,17 +7,184 @@
|
|||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/device.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
BLOCKING_NOTIFIER_HEAD(scmi_requested_devices_nh);
|
||||
EXPORT_SYMBOL_GPL(scmi_requested_devices_nh);
|
||||
|
||||
static DEFINE_IDA(scmi_bus_id);
|
||||
static DEFINE_IDR(scmi_protocols);
|
||||
static DEFINE_SPINLOCK(protocol_lock);
|
||||
|
||||
static DEFINE_IDR(scmi_requested_devices);
|
||||
/* Protect access to scmi_requested_devices */
|
||||
static DEFINE_MUTEX(scmi_requested_devices_mtx);
|
||||
|
||||
struct scmi_requested_dev {
|
||||
const struct scmi_device_id *id_table;
|
||||
struct list_head node;
|
||||
};
|
||||
|
||||
/* Track globally the creation of SCMI SystemPower related devices */
|
||||
static atomic_t scmi_syspower_registered = ATOMIC_INIT(0);
|
||||
|
||||
/**
|
||||
* scmi_protocol_device_request - Helper to request a device
|
||||
*
|
||||
* @id_table: A protocol/name pair descriptor for the device to be created.
|
||||
*
|
||||
* This helper let an SCMI driver request specific devices identified by the
|
||||
* @id_table to be created for each active SCMI instance.
|
||||
*
|
||||
* The requested device name MUST NOT be already existent for any protocol;
|
||||
* at first the freshly requested @id_table is annotated in the IDR table
|
||||
* @scmi_requested_devices and then the requested device is advertised to any
|
||||
* registered party via the @scmi_requested_devices_nh notification chain.
|
||||
*
|
||||
* Return: 0 on Success
|
||||
*/
|
||||
static int scmi_protocol_device_request(const struct scmi_device_id *id_table)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned int id = 0;
|
||||
struct list_head *head, *phead = NULL;
|
||||
struct scmi_requested_dev *rdev;
|
||||
|
||||
pr_debug("Requesting SCMI device (%s) for protocol %x\n",
|
||||
id_table->name, id_table->protocol_id);
|
||||
|
||||
if (IS_ENABLED(CONFIG_ARM_SCMI_RAW_MODE_SUPPORT) &&
|
||||
!IS_ENABLED(CONFIG_ARM_SCMI_RAW_MODE_SUPPORT_COEX)) {
|
||||
pr_warn("SCMI Raw mode active. Rejecting '%s'/0x%02X\n",
|
||||
id_table->name, id_table->protocol_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Search for the matching protocol rdev list and then search
|
||||
* of any existent equally named device...fails if any duplicate found.
|
||||
*/
|
||||
mutex_lock(&scmi_requested_devices_mtx);
|
||||
idr_for_each_entry(&scmi_requested_devices, head, id) {
|
||||
if (!phead) {
|
||||
/* A list found registered in the IDR is never empty */
|
||||
rdev = list_first_entry(head, struct scmi_requested_dev,
|
||||
node);
|
||||
if (rdev->id_table->protocol_id ==
|
||||
id_table->protocol_id)
|
||||
phead = head;
|
||||
}
|
||||
list_for_each_entry(rdev, head, node) {
|
||||
if (!strcmp(rdev->id_table->name, id_table->name)) {
|
||||
pr_err("Ignoring duplicate request [%d] %s\n",
|
||||
rdev->id_table->protocol_id,
|
||||
rdev->id_table->name);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* No duplicate found for requested id_table, so let's create a new
|
||||
* requested device entry for this new valid request.
|
||||
*/
|
||||
rdev = kzalloc(sizeof(*rdev), GFP_KERNEL);
|
||||
if (!rdev) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
rdev->id_table = id_table;
|
||||
|
||||
/*
|
||||
* Append the new requested device table descriptor to the head of the
|
||||
* related protocol list, eventually creating such head if not already
|
||||
* there.
|
||||
*/
|
||||
if (!phead) {
|
||||
phead = kzalloc(sizeof(*phead), GFP_KERNEL);
|
||||
if (!phead) {
|
||||
kfree(rdev);
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
INIT_LIST_HEAD(phead);
|
||||
|
||||
ret = idr_alloc(&scmi_requested_devices, (void *)phead,
|
||||
id_table->protocol_id,
|
||||
id_table->protocol_id + 1, GFP_KERNEL);
|
||||
if (ret != id_table->protocol_id) {
|
||||
pr_err("Failed to save SCMI device - ret:%d\n", ret);
|
||||
kfree(rdev);
|
||||
kfree(phead);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
ret = 0;
|
||||
}
|
||||
list_add(&rdev->node, phead);
|
||||
|
||||
out:
|
||||
mutex_unlock(&scmi_requested_devices_mtx);
|
||||
|
||||
if (!ret)
|
||||
blocking_notifier_call_chain(&scmi_requested_devices_nh,
|
||||
SCMI_BUS_NOTIFY_DEVICE_REQUEST,
|
||||
(void *)rdev->id_table);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* scmi_protocol_device_unrequest - Helper to unrequest a device
|
||||
*
|
||||
* @id_table: A protocol/name pair descriptor for the device to be unrequested.
|
||||
*
|
||||
* The unrequested device, described by the provided id_table, is at first
|
||||
* removed from the IDR @scmi_requested_devices and then the removal is
|
||||
* advertised to any registered party via the @scmi_requested_devices_nh
|
||||
* notification chain.
|
||||
*/
|
||||
static void scmi_protocol_device_unrequest(const struct scmi_device_id *id_table)
|
||||
{
|
||||
struct list_head *phead;
|
||||
|
||||
pr_debug("Unrequesting SCMI device (%s) for protocol %x\n",
|
||||
id_table->name, id_table->protocol_id);
|
||||
|
||||
mutex_lock(&scmi_requested_devices_mtx);
|
||||
phead = idr_find(&scmi_requested_devices, id_table->protocol_id);
|
||||
if (phead) {
|
||||
struct scmi_requested_dev *victim, *tmp;
|
||||
|
||||
list_for_each_entry_safe(victim, tmp, phead, node) {
|
||||
if (!strcmp(victim->id_table->name, id_table->name)) {
|
||||
list_del(&victim->node);
|
||||
|
||||
mutex_unlock(&scmi_requested_devices_mtx);
|
||||
blocking_notifier_call_chain(&scmi_requested_devices_nh,
|
||||
SCMI_BUS_NOTIFY_DEVICE_UNREQUEST,
|
||||
(void *)victim->id_table);
|
||||
kfree(victim);
|
||||
mutex_lock(&scmi_requested_devices_mtx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (list_empty(phead)) {
|
||||
idr_remove(&scmi_requested_devices,
|
||||
id_table->protocol_id);
|
||||
kfree(phead);
|
||||
}
|
||||
}
|
||||
mutex_unlock(&scmi_requested_devices_mtx);
|
||||
}
|
||||
|
||||
static const struct scmi_device_id *
|
||||
scmi_dev_match_id(struct scmi_device *scmi_dev, struct scmi_driver *scmi_drv)
|
||||
|
|
@ -57,11 +224,11 @@ static int scmi_match_by_id_table(struct device *dev, void *data)
|
|||
struct scmi_device_id *id_table = data;
|
||||
|
||||
return sdev->protocol_id == id_table->protocol_id &&
|
||||
!strcmp(sdev->name, id_table->name);
|
||||
(id_table->name && !strcmp(sdev->name, id_table->name));
|
||||
}
|
||||
|
||||
struct scmi_device *scmi_child_dev_find(struct device *parent,
|
||||
int prot_id, const char *name)
|
||||
static struct scmi_device *scmi_child_dev_find(struct device *parent,
|
||||
int prot_id, const char *name)
|
||||
{
|
||||
struct scmi_device_id id_table;
|
||||
struct device *dev;
|
||||
|
|
@ -76,30 +243,6 @@ struct scmi_device *scmi_child_dev_find(struct device *parent,
|
|||
return to_scmi_dev(dev);
|
||||
}
|
||||
|
||||
const struct scmi_protocol *scmi_protocol_get(int protocol_id)
|
||||
{
|
||||
const struct scmi_protocol *proto;
|
||||
|
||||
proto = idr_find(&scmi_protocols, protocol_id);
|
||||
if (!proto || !try_module_get(proto->owner)) {
|
||||
pr_warn("SCMI Protocol 0x%x not found!\n", protocol_id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pr_debug("Found SCMI Protocol 0x%x\n", protocol_id);
|
||||
|
||||
return proto;
|
||||
}
|
||||
|
||||
void scmi_protocol_put(int protocol_id)
|
||||
{
|
||||
const struct scmi_protocol *proto;
|
||||
|
||||
proto = idr_find(&scmi_protocols, protocol_id);
|
||||
if (proto)
|
||||
module_put(proto->owner);
|
||||
}
|
||||
|
||||
static int scmi_dev_probe(struct device *dev)
|
||||
{
|
||||
struct scmi_driver *scmi_drv = to_scmi_driver(dev->driver);
|
||||
|
|
@ -120,12 +263,13 @@ static void scmi_dev_remove(struct device *dev)
|
|||
scmi_drv->remove(scmi_dev);
|
||||
}
|
||||
|
||||
static struct bus_type scmi_bus_type = {
|
||||
struct bus_type scmi_bus_type = {
|
||||
.name = "scmi_protocol",
|
||||
.match = scmi_dev_match,
|
||||
.probe = scmi_dev_probe,
|
||||
.remove = scmi_dev_remove,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(scmi_bus_type);
|
||||
|
||||
int scmi_driver_register(struct scmi_driver *driver, struct module *owner,
|
||||
const char *mod_name)
|
||||
|
|
@ -146,7 +290,7 @@ int scmi_driver_register(struct scmi_driver *driver, struct module *owner,
|
|||
|
||||
retval = driver_register(&driver->driver);
|
||||
if (!retval)
|
||||
pr_debug("registered new scmi driver %s\n", driver->name);
|
||||
pr_debug("Registered new scmi driver %s\n", driver->name);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
|
@ -164,13 +308,53 @@ static void scmi_device_release(struct device *dev)
|
|||
kfree(to_scmi_dev(dev));
|
||||
}
|
||||
|
||||
struct scmi_device *
|
||||
scmi_device_create(struct device_node *np, struct device *parent, int protocol,
|
||||
const char *name)
|
||||
static void __scmi_device_destroy(struct scmi_device *scmi_dev)
|
||||
{
|
||||
pr_debug("(%s) Destroying SCMI device '%s' for protocol 0x%x (%s)\n",
|
||||
of_node_full_name(scmi_dev->dev.parent->of_node),
|
||||
dev_name(&scmi_dev->dev), scmi_dev->protocol_id,
|
||||
scmi_dev->name);
|
||||
|
||||
if (scmi_dev->protocol_id == SCMI_PROTOCOL_SYSTEM)
|
||||
atomic_set(&scmi_syspower_registered, 0);
|
||||
|
||||
kfree_const(scmi_dev->name);
|
||||
ida_free(&scmi_bus_id, scmi_dev->id);
|
||||
device_unregister(&scmi_dev->dev);
|
||||
}
|
||||
|
||||
static struct scmi_device *
|
||||
__scmi_device_create(struct device_node *np, struct device *parent,
|
||||
int protocol, const char *name)
|
||||
{
|
||||
int id, retval;
|
||||
struct scmi_device *scmi_dev;
|
||||
|
||||
/*
|
||||
* If the same protocol/name device already exist under the same parent
|
||||
* (i.e. SCMI instance) just return the existent device.
|
||||
* This avoids any race between the SCMI driver, creating devices for
|
||||
* each DT defined protocol at probe time, and the concurrent
|
||||
* registration of SCMI drivers.
|
||||
*/
|
||||
scmi_dev = scmi_child_dev_find(parent, protocol, name);
|
||||
if (scmi_dev)
|
||||
return scmi_dev;
|
||||
|
||||
/*
|
||||
* Ignore any possible subsequent failures while creating the device
|
||||
* since we are doomed anyway at that point; not using a mutex which
|
||||
* spans across this whole function to keep things simple and to avoid
|
||||
* to serialize all the __scmi_device_create calls across possibly
|
||||
* different SCMI server instances (parent)
|
||||
*/
|
||||
if (protocol == SCMI_PROTOCOL_SYSTEM &&
|
||||
atomic_cmpxchg(&scmi_syspower_registered, 0, 1)) {
|
||||
dev_warn(parent,
|
||||
"SCMI SystemPower protocol device must be unique !\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
scmi_dev = kzalloc(sizeof(*scmi_dev), GFP_KERNEL);
|
||||
if (!scmi_dev)
|
||||
return NULL;
|
||||
|
|
@ -200,6 +384,10 @@ scmi_device_create(struct device_node *np, struct device *parent, int protocol,
|
|||
if (retval)
|
||||
goto put_dev;
|
||||
|
||||
pr_debug("(%s) Created SCMI device '%s' for protocol 0x%x (%s)\n",
|
||||
of_node_full_name(parent->of_node),
|
||||
dev_name(&scmi_dev->dev), protocol, name);
|
||||
|
||||
return scmi_dev;
|
||||
put_dev:
|
||||
kfree_const(scmi_dev->name);
|
||||
|
|
@ -208,77 +396,85 @@ scmi_device_create(struct device_node *np, struct device *parent, int protocol,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
void scmi_device_destroy(struct scmi_device *scmi_dev)
|
||||
/**
|
||||
* scmi_device_create - A method to create one or more SCMI devices
|
||||
*
|
||||
* @np: A reference to the device node to use for the new device(s)
|
||||
* @parent: The parent device to use identifying a specific SCMI instance
|
||||
* @protocol: The SCMI protocol to be associated with this device
|
||||
* @name: The requested-name of the device to be created; this is optional
|
||||
* and if no @name is provided, all the devices currently known to
|
||||
* be requested on the SCMI bus for @protocol will be created.
|
||||
*
|
||||
* This method can be invoked to create a single well-defined device (like
|
||||
* a transport device or a device requested by an SCMI driver loaded after
|
||||
* the core SCMI stack has been probed), or to create all the devices currently
|
||||
* known to have been requested by the loaded SCMI drivers for a specific
|
||||
* protocol (typically during SCMI core protocol enumeration at probe time).
|
||||
*
|
||||
* Return: The created device (or one of them if @name was NOT provided and
|
||||
* multiple devices were created) or NULL if no device was created;
|
||||
* note that NULL indicates an error ONLY in case a specific @name
|
||||
* was provided: when @name param was not provided, a number of devices
|
||||
* could have been potentially created for a whole protocol, unless no
|
||||
* device was found to have been requested for that specific protocol.
|
||||
*/
|
||||
struct scmi_device *scmi_device_create(struct device_node *np,
|
||||
struct device *parent, int protocol,
|
||||
const char *name)
|
||||
{
|
||||
kfree_const(scmi_dev->name);
|
||||
scmi_handle_put(scmi_dev->handle);
|
||||
ida_free(&scmi_bus_id, scmi_dev->id);
|
||||
device_unregister(&scmi_dev->dev);
|
||||
}
|
||||
struct list_head *phead;
|
||||
struct scmi_requested_dev *rdev;
|
||||
struct scmi_device *scmi_dev = NULL;
|
||||
|
||||
void scmi_device_link_add(struct device *consumer, struct device *supplier)
|
||||
{
|
||||
struct device_link *link;
|
||||
if (name)
|
||||
return __scmi_device_create(np, parent, protocol, name);
|
||||
|
||||
link = device_link_add(consumer, supplier, DL_FLAG_AUTOREMOVE_CONSUMER);
|
||||
|
||||
WARN_ON(!link);
|
||||
}
|
||||
|
||||
void scmi_set_handle(struct scmi_device *scmi_dev)
|
||||
{
|
||||
scmi_dev->handle = scmi_handle_get(&scmi_dev->dev);
|
||||
if (scmi_dev->handle)
|
||||
scmi_device_link_add(&scmi_dev->dev, scmi_dev->handle->dev);
|
||||
}
|
||||
|
||||
int scmi_protocol_register(const struct scmi_protocol *proto)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!proto) {
|
||||
pr_err("invalid protocol\n");
|
||||
return -EINVAL;
|
||||
mutex_lock(&scmi_requested_devices_mtx);
|
||||
phead = idr_find(&scmi_requested_devices, protocol);
|
||||
/* Nothing to do. */
|
||||
if (!phead) {
|
||||
mutex_unlock(&scmi_requested_devices_mtx);
|
||||
return scmi_dev;
|
||||
}
|
||||
|
||||
if (!proto->instance_init) {
|
||||
pr_err("missing init for protocol 0x%x\n", proto->id);
|
||||
return -EINVAL;
|
||||
/* Walk the list of requested devices for protocol and create them */
|
||||
list_for_each_entry(rdev, phead, node) {
|
||||
struct scmi_device *sdev;
|
||||
|
||||
sdev = __scmi_device_create(np, parent,
|
||||
rdev->id_table->protocol_id,
|
||||
rdev->id_table->name);
|
||||
/* Report errors and carry on... */
|
||||
if (sdev)
|
||||
scmi_dev = sdev;
|
||||
else
|
||||
pr_err("(%s) Failed to create device for protocol 0x%x (%s)\n",
|
||||
of_node_full_name(parent->of_node),
|
||||
rdev->id_table->protocol_id,
|
||||
rdev->id_table->name);
|
||||
}
|
||||
mutex_unlock(&scmi_requested_devices_mtx);
|
||||
|
||||
spin_lock(&protocol_lock);
|
||||
ret = idr_alloc(&scmi_protocols, (void *)proto,
|
||||
proto->id, proto->id + 1, GFP_ATOMIC);
|
||||
spin_unlock(&protocol_lock);
|
||||
if (ret != proto->id) {
|
||||
pr_err("unable to allocate SCMI idr slot for 0x%x - err %d\n",
|
||||
proto->id, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
pr_debug("Registered SCMI Protocol 0x%x\n", proto->id);
|
||||
|
||||
return 0;
|
||||
return scmi_dev;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(scmi_protocol_register);
|
||||
EXPORT_SYMBOL_GPL(scmi_device_create);
|
||||
|
||||
void scmi_protocol_unregister(const struct scmi_protocol *proto)
|
||||
void scmi_device_destroy(struct device *parent, int protocol, const char *name)
|
||||
{
|
||||
spin_lock(&protocol_lock);
|
||||
idr_remove(&scmi_protocols, proto->id);
|
||||
spin_unlock(&protocol_lock);
|
||||
struct scmi_device *scmi_dev;
|
||||
|
||||
pr_debug("Unregistered SCMI Protocol 0x%x\n", proto->id);
|
||||
|
||||
return;
|
||||
scmi_dev = scmi_child_dev_find(parent, protocol, name);
|
||||
if (scmi_dev)
|
||||
__scmi_device_destroy(scmi_dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(scmi_protocol_unregister);
|
||||
EXPORT_SYMBOL_GPL(scmi_device_destroy);
|
||||
|
||||
static int __scmi_devices_unregister(struct device *dev, void *data)
|
||||
{
|
||||
struct scmi_device *scmi_dev = to_scmi_dev(dev);
|
||||
|
||||
scmi_device_destroy(scmi_dev);
|
||||
__scmi_device_destroy(scmi_dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -287,20 +483,33 @@ static void scmi_devices_unregister(void)
|
|||
bus_for_each_dev(&scmi_bus_type, NULL, NULL, __scmi_devices_unregister);
|
||||
}
|
||||
|
||||
int __init scmi_bus_init(void)
|
||||
static int __init scmi_bus_init(void)
|
||||
{
|
||||
int retval;
|
||||
|
||||
retval = bus_register(&scmi_bus_type);
|
||||
if (retval)
|
||||
pr_err("scmi protocol bus register failed (%d)\n", retval);
|
||||
pr_err("SCMI protocol bus register failed (%d)\n", retval);
|
||||
|
||||
pr_info("SCMI protocol bus registered\n");
|
||||
|
||||
return retval;
|
||||
}
|
||||
subsys_initcall(scmi_bus_init);
|
||||
|
||||
void __exit scmi_bus_exit(void)
|
||||
static void __exit scmi_bus_exit(void)
|
||||
{
|
||||
/*
|
||||
* Destroy all remaining devices: just in case the drivers were
|
||||
* manually unbound and at first and then the modules unloaded.
|
||||
*/
|
||||
scmi_devices_unregister();
|
||||
bus_unregister(&scmi_bus_type);
|
||||
ida_destroy(&scmi_bus_id);
|
||||
}
|
||||
module_exit(scmi_bus_exit);
|
||||
|
||||
MODULE_ALIAS("scmi-core");
|
||||
MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");
|
||||
MODULE_DESCRIPTION("ARM SCMI protocol bus");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
|||
|
|
@ -27,6 +27,48 @@
|
|||
#include "protocols.h"
|
||||
#include "notify.h"
|
||||
|
||||
#define SCMI_MAX_CHANNELS 256
|
||||
|
||||
#define SCMI_MAX_RESPONSE_TIMEOUT (2 * MSEC_PER_SEC)
|
||||
|
||||
enum scmi_error_codes {
|
||||
SCMI_SUCCESS = 0, /* Success */
|
||||
SCMI_ERR_SUPPORT = -1, /* Not supported */
|
||||
SCMI_ERR_PARAMS = -2, /* Invalid Parameters */
|
||||
SCMI_ERR_ACCESS = -3, /* Invalid access/permission denied */
|
||||
SCMI_ERR_ENTRY = -4, /* Not found */
|
||||
SCMI_ERR_RANGE = -5, /* Value out of range */
|
||||
SCMI_ERR_BUSY = -6, /* Device busy */
|
||||
SCMI_ERR_COMMS = -7, /* Communication Error */
|
||||
SCMI_ERR_GENERIC = -8, /* Generic Error */
|
||||
SCMI_ERR_HARDWARE = -9, /* Hardware Error */
|
||||
SCMI_ERR_PROTOCOL = -10,/* Protocol Error */
|
||||
};
|
||||
|
||||
static const int scmi_linux_errmap[] = {
|
||||
/* better than switch case as long as return value is continuous */
|
||||
0, /* SCMI_SUCCESS */
|
||||
-EOPNOTSUPP, /* SCMI_ERR_SUPPORT */
|
||||
-EINVAL, /* SCMI_ERR_PARAM */
|
||||
-EACCES, /* SCMI_ERR_ACCESS */
|
||||
-ENOENT, /* SCMI_ERR_ENTRY */
|
||||
-ERANGE, /* SCMI_ERR_RANGE */
|
||||
-EBUSY, /* SCMI_ERR_BUSY */
|
||||
-ECOMM, /* SCMI_ERR_COMMS */
|
||||
-EIO, /* SCMI_ERR_GENERIC */
|
||||
-EREMOTEIO, /* SCMI_ERR_HARDWARE */
|
||||
-EPROTO, /* SCMI_ERR_PROTOCOL */
|
||||
};
|
||||
|
||||
static inline int scmi_to_linux_errno(int errno)
|
||||
{
|
||||
int err_idx = -errno;
|
||||
|
||||
if (err_idx >= SCMI_SUCCESS && err_idx < ARRAY_SIZE(scmi_linux_errmap))
|
||||
return scmi_linux_errmap[err_idx];
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
#define MSG_ID_MASK GENMASK(7, 0)
|
||||
#define MSG_XTRACT_ID(hdr) FIELD_GET(MSG_ID_MASK, (hdr))
|
||||
#define MSG_TYPE_MASK GENMASK(9, 8)
|
||||
|
|
@ -96,18 +138,19 @@ static inline void unpack_scmi_header(u32 msg_hdr, struct scmi_msg_hdr *hdr)
|
|||
|
||||
struct scmi_revision_info *
|
||||
scmi_revision_area_get(const struct scmi_protocol_handle *ph);
|
||||
int scmi_handle_put(const struct scmi_handle *handle);
|
||||
void scmi_device_link_add(struct device *consumer, struct device *supplier);
|
||||
struct scmi_handle *scmi_handle_get(struct device *dev);
|
||||
void scmi_set_handle(struct scmi_device *scmi_dev);
|
||||
void scmi_setup_protocol_implemented(const struct scmi_protocol_handle *ph,
|
||||
u8 *prot_imp);
|
||||
|
||||
int __init scmi_bus_init(void);
|
||||
void __exit scmi_bus_exit(void);
|
||||
extern struct bus_type scmi_bus_type;
|
||||
|
||||
const struct scmi_protocol *scmi_protocol_get(int protocol_id);
|
||||
void scmi_protocol_put(int protocol_id);
|
||||
#define SCMI_BUS_NOTIFY_DEVICE_REQUEST 0
|
||||
#define SCMI_BUS_NOTIFY_DEVICE_UNREQUEST 1
|
||||
extern struct blocking_notifier_head scmi_requested_devices_nh;
|
||||
|
||||
struct scmi_device *scmi_device_create(struct device_node *np,
|
||||
struct device *parent, int protocol,
|
||||
const char *name);
|
||||
void scmi_device_destroy(struct device *parent, int protocol, const char *name);
|
||||
|
||||
int scmi_protocol_acquire(const struct scmi_handle *handle, u8 protocol_id);
|
||||
void scmi_protocol_release(const struct scmi_handle *handle, u8 protocol_id);
|
||||
|
|
@ -116,6 +159,8 @@ void scmi_protocol_release(const struct scmi_handle *handle, u8 protocol_id);
|
|||
/**
|
||||
* struct scmi_chan_info - Structure representing a SCMI channel information
|
||||
*
|
||||
* @id: An identifier for this channel: this matches the protocol number
|
||||
* used to initialize this channel
|
||||
* @dev: Reference to device in the SCMI hierarchy corresponding to this
|
||||
* channel
|
||||
* @rx_timeout_ms: The configured RX timeout in milliseconds.
|
||||
|
|
@ -127,6 +172,7 @@ void scmi_protocol_release(const struct scmi_handle *handle, u8 protocol_id);
|
|||
* @transport_info: Transport layer related information
|
||||
*/
|
||||
struct scmi_chan_info {
|
||||
int id;
|
||||
struct device *dev;
|
||||
unsigned int rx_timeout_ms;
|
||||
struct scmi_handle *handle;
|
||||
|
|
@ -153,7 +199,7 @@ struct scmi_chan_info {
|
|||
*/
|
||||
struct scmi_transport_ops {
|
||||
int (*link_supplier)(struct device *dev);
|
||||
bool (*chan_available)(struct device *dev, int idx);
|
||||
bool (*chan_available)(struct device_node *of_node, int idx);
|
||||
int (*chan_setup)(struct scmi_chan_info *cinfo, struct device *dev,
|
||||
bool tx);
|
||||
int (*chan_free)(int id, void *p, void *data);
|
||||
|
|
@ -170,11 +216,6 @@ struct scmi_transport_ops {
|
|||
bool (*poll_done)(struct scmi_chan_info *cinfo, struct scmi_xfer *xfer);
|
||||
};
|
||||
|
||||
int scmi_protocol_device_request(const struct scmi_device_id *id_table);
|
||||
void scmi_protocol_device_unrequest(const struct scmi_device_id *id_table);
|
||||
struct scmi_device *scmi_child_dev_find(struct device *parent,
|
||||
int prot_id, const char *name);
|
||||
|
||||
/**
|
||||
* struct scmi_desc - Description of SoC integration
|
||||
*
|
||||
|
|
@ -215,6 +256,36 @@ struct scmi_desc {
|
|||
const bool atomic_enabled;
|
||||
};
|
||||
|
||||
static inline bool is_polling_required(struct scmi_chan_info *cinfo,
|
||||
const struct scmi_desc *desc)
|
||||
{
|
||||
return cinfo->no_completion_irq || desc->force_polling;
|
||||
}
|
||||
|
||||
static inline bool is_transport_polling_capable(const struct scmi_desc *desc)
|
||||
{
|
||||
return desc->ops->poll_done || desc->sync_cmds_completed_on_ret;
|
||||
}
|
||||
|
||||
static inline bool is_polling_enabled(struct scmi_chan_info *cinfo,
|
||||
const struct scmi_desc *desc)
|
||||
{
|
||||
return is_polling_required(cinfo, desc) &&
|
||||
is_transport_polling_capable(desc);
|
||||
}
|
||||
|
||||
void scmi_xfer_raw_put(const struct scmi_handle *handle,
|
||||
struct scmi_xfer *xfer);
|
||||
struct scmi_xfer *scmi_xfer_raw_get(const struct scmi_handle *handle);
|
||||
struct scmi_chan_info *
|
||||
scmi_xfer_raw_channel_get(const struct scmi_handle *handle, u8 protocol_id);
|
||||
|
||||
int scmi_xfer_raw_inflight_register(const struct scmi_handle *handle,
|
||||
struct scmi_xfer *xfer);
|
||||
|
||||
int scmi_xfer_raw_wait_for_message_response(struct scmi_chan_info *cinfo,
|
||||
struct scmi_xfer *xfer,
|
||||
unsigned int timeout_ms);
|
||||
#ifdef CONFIG_ARM_SCMI_TRANSPORT_MAILBOX
|
||||
extern const struct scmi_desc scmi_mailbox_desc;
|
||||
#endif
|
||||
|
|
@ -229,7 +300,6 @@ extern const struct scmi_desc scmi_optee_desc;
|
|||
#endif
|
||||
|
||||
void scmi_rx_callback(struct scmi_chan_info *cinfo, u32 msg_hdr, void *priv);
|
||||
void scmi_free_channel(struct scmi_chan_info *cinfo, struct idr *idr, int id);
|
||||
|
||||
/* shmem related declarations */
|
||||
struct scmi_shared_mem;
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -46,9 +46,9 @@ static void rx_callback(struct mbox_client *cl, void *m)
|
|||
scmi_rx_callback(smbox->cinfo, shmem_read_header(smbox->shmem), NULL);
|
||||
}
|
||||
|
||||
static bool mailbox_chan_available(struct device *dev, int idx)
|
||||
static bool mailbox_chan_available(struct device_node *of_node, int idx)
|
||||
{
|
||||
return !of_parse_phandle_with_args(dev->of_node, "mboxes",
|
||||
return !of_parse_phandle_with_args(of_node, "mboxes",
|
||||
"#mbox-cells", idx, NULL);
|
||||
}
|
||||
|
||||
|
|
@ -120,8 +120,6 @@ static int mailbox_chan_free(int id, void *p, void *data)
|
|||
smbox->cinfo = NULL;
|
||||
}
|
||||
|
||||
scmi_free_channel(cinfo, data, id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -328,11 +328,11 @@ static int scmi_optee_link_supplier(struct device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static bool scmi_optee_chan_available(struct device *dev, int idx)
|
||||
static bool scmi_optee_chan_available(struct device_node *of_node, int idx)
|
||||
{
|
||||
u32 channel_id;
|
||||
|
||||
return !of_property_read_u32_index(dev->of_node, "linaro,optee-channel-id",
|
||||
return !of_property_read_u32_index(of_node, "linaro,optee-channel-id",
|
||||
idx, &channel_id);
|
||||
}
|
||||
|
||||
|
|
@ -481,8 +481,6 @@ static int scmi_optee_chan_free(int id, void *p, void *data)
|
|||
cinfo->transport_info = NULL;
|
||||
channel->cinfo = NULL;
|
||||
|
||||
scmi_free_channel(cinfo, data, id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -115,6 +115,7 @@ struct scmi_msg_hdr {
|
|||
* - SCMI_XFER_SENT_OK -> SCMI_XFER_RESP_OK [ -> SCMI_XFER_DRESP_OK ]
|
||||
* - SCMI_XFER_SENT_OK -> SCMI_XFER_DRESP_OK
|
||||
* (Missing synchronous response is assumed OK and ignored)
|
||||
* @flags: Optional flags associated to this xfer.
|
||||
* @lock: A spinlock to protect state and busy fields.
|
||||
* @priv: A pointer for transport private usage.
|
||||
*/
|
||||
|
|
@ -135,6 +136,12 @@ struct scmi_xfer {
|
|||
#define SCMI_XFER_RESP_OK 1
|
||||
#define SCMI_XFER_DRESP_OK 2
|
||||
int state;
|
||||
#define SCMI_XFER_FLAG_IS_RAW BIT(0)
|
||||
#define SCMI_XFER_IS_RAW(x) ((x)->flags & SCMI_XFER_FLAG_IS_RAW)
|
||||
#define SCMI_XFER_FLAG_CHAN_SET BIT(1)
|
||||
#define SCMI_XFER_IS_CHAN_SET(x) \
|
||||
((x)->flags & SCMI_XFER_FLAG_CHAN_SET)
|
||||
int flags;
|
||||
/* A lock to protect state and busy fields */
|
||||
spinlock_t lock;
|
||||
void *priv;
|
||||
|
|
|
|||
1443
drivers/firmware/arm_scmi/raw_mode.c
Normal file
1443
drivers/firmware/arm_scmi/raw_mode.c
Normal file
File diff suppressed because it is too large
Load Diff
31
drivers/firmware/arm_scmi/raw_mode.h
Normal file
31
drivers/firmware/arm_scmi/raw_mode.h
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* System Control and Management Interface (SCMI) Message Protocol
|
||||
* Raw mode support header.
|
||||
*
|
||||
* Copyright (C) 2022 ARM Ltd.
|
||||
*/
|
||||
#ifndef _SCMI_RAW_MODE_H
|
||||
#define _SCMI_RAW_MODE_H
|
||||
|
||||
#include "common.h"
|
||||
|
||||
enum {
|
||||
SCMI_RAW_REPLY_QUEUE,
|
||||
SCMI_RAW_NOTIF_QUEUE,
|
||||
SCMI_RAW_ERRS_QUEUE,
|
||||
SCMI_RAW_MAX_QUEUE
|
||||
};
|
||||
|
||||
void *scmi_raw_mode_init(const struct scmi_handle *handle,
|
||||
struct dentry *top_dentry, int instance_id,
|
||||
u8 *channels, int num_chans,
|
||||
const struct scmi_desc *desc, int tx_max_msg);
|
||||
void scmi_raw_mode_cleanup(void *raw);
|
||||
|
||||
void scmi_raw_message_report(void *raw, struct scmi_xfer *xfer,
|
||||
unsigned int idx, unsigned int chan_id);
|
||||
void scmi_raw_error_report(void *raw, struct scmi_chan_info *cinfo,
|
||||
u32 msg_hdr, void *priv);
|
||||
|
||||
#endif /* _SCMI_RAW_MODE_H */
|
||||
|
|
@ -81,10 +81,11 @@ u32 shmem_read_header(struct scmi_shared_mem __iomem *shmem)
|
|||
void shmem_fetch_response(struct scmi_shared_mem __iomem *shmem,
|
||||
struct scmi_xfer *xfer)
|
||||
{
|
||||
size_t len = ioread32(&shmem->length);
|
||||
|
||||
xfer->hdr.status = ioread32(shmem->msg_payload);
|
||||
/* Skip the length of header and status in shmem area i.e 8 bytes */
|
||||
xfer->rx.len = min_t(size_t, xfer->rx.len,
|
||||
ioread32(&shmem->length) - 8);
|
||||
xfer->rx.len = min_t(size_t, xfer->rx.len, len > 8 ? len - 8 : 0);
|
||||
|
||||
/* Take a copy to the rx buffer.. */
|
||||
memcpy_fromio(xfer->rx.buf, shmem->msg_payload + 4, xfer->rx.len);
|
||||
|
|
@ -93,8 +94,10 @@ void shmem_fetch_response(struct scmi_shared_mem __iomem *shmem,
|
|||
void shmem_fetch_notification(struct scmi_shared_mem __iomem *shmem,
|
||||
size_t max_len, struct scmi_xfer *xfer)
|
||||
{
|
||||
size_t len = ioread32(&shmem->length);
|
||||
|
||||
/* Skip only the length of header in shmem area i.e 4 bytes */
|
||||
xfer->rx.len = min_t(size_t, max_len, ioread32(&shmem->length) - 4);
|
||||
xfer->rx.len = min_t(size_t, max_len, len > 4 ? len - 4 : 0);
|
||||
|
||||
/* Take a copy to the rx buffer.. */
|
||||
memcpy_fromio(xfer->rx.buf, shmem->msg_payload, xfer->rx.len);
|
||||
|
|
|
|||
|
|
@ -52,9 +52,9 @@ static irqreturn_t smc_msg_done_isr(int irq, void *data)
|
|||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static bool smc_chan_available(struct device *dev, int idx)
|
||||
static bool smc_chan_available(struct device_node *of_node, int idx)
|
||||
{
|
||||
struct device_node *np = of_parse_phandle(dev->of_node, "shmem", 0);
|
||||
struct device_node *np = of_parse_phandle(of_node, "shmem", 0);
|
||||
if (!np)
|
||||
return false;
|
||||
|
||||
|
|
@ -171,8 +171,6 @@ static int smc_chan_free(int id, void *p, void *data)
|
|||
cinfo->transport_info = NULL;
|
||||
scmi_info->cinfo = NULL;
|
||||
|
||||
scmi_free_channel(cinfo, data, id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -160,7 +160,6 @@ static void scmi_vio_channel_cleanup_sync(struct scmi_vio_channel *vioch)
|
|||
}
|
||||
|
||||
vioch->shutdown_done = &vioch_shutdown_done;
|
||||
virtio_break_device(vioch->vqueue->vdev);
|
||||
if (!vioch->is_rx && vioch->deferred_tx_wq)
|
||||
/* Cannot be kicked anymore after this...*/
|
||||
vioch->deferred_tx_wq = NULL;
|
||||
|
|
@ -386,7 +385,7 @@ static int virtio_link_supplier(struct device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static bool virtio_chan_available(struct device *dev, int idx)
|
||||
static bool virtio_chan_available(struct device_node *of_node, int idx)
|
||||
{
|
||||
struct scmi_vio_channel *channels, *vioch = NULL;
|
||||
|
||||
|
|
@ -482,10 +481,14 @@ static int virtio_chan_free(int id, void *p, void *data)
|
|||
struct scmi_chan_info *cinfo = p;
|
||||
struct scmi_vio_channel *vioch = cinfo->transport_info;
|
||||
|
||||
/*
|
||||
* Break device to inhibit further traffic flowing while shutting down
|
||||
* the channels: doing it later holding vioch->lock creates unsafe
|
||||
* locking dependency chains as reported by LOCKDEP.
|
||||
*/
|
||||
virtio_break_device(vioch->vqueue->vdev);
|
||||
scmi_vio_channel_cleanup_sync(vioch);
|
||||
|
||||
scmi_free_channel(cinfo, data, id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -899,6 +899,7 @@ ssize_t debugfs_read_file_str(struct file *file, char __user *user_buf,
|
|||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(debugfs_create_str);
|
||||
|
||||
static ssize_t debugfs_write_file_str(struct file *file, const char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
|
|
|
|||
|
|
@ -804,11 +804,6 @@ struct scmi_device {
|
|||
|
||||
#define to_scmi_dev(d) container_of(d, struct scmi_device, dev)
|
||||
|
||||
struct scmi_device *
|
||||
scmi_device_create(struct device_node *np, struct device *parent, int protocol,
|
||||
const char *name);
|
||||
void scmi_device_destroy(struct scmi_device *scmi_dev);
|
||||
|
||||
struct scmi_device_id {
|
||||
u8 protocol_id;
|
||||
const char *name;
|
||||
|
|
|
|||
|
|
@ -139,11 +139,15 @@ TRACE_EVENT(scmi_rx_done,
|
|||
);
|
||||
|
||||
TRACE_EVENT(scmi_msg_dump,
|
||||
TP_PROTO(u8 protocol_id, u8 msg_id, unsigned char *tag, u16 seq,
|
||||
int status, void *buf, size_t len),
|
||||
TP_ARGS(protocol_id, msg_id, tag, seq, status, buf, len),
|
||||
TP_PROTO(int id, u8 channel_id, u8 protocol_id, u8 msg_id,
|
||||
unsigned char *tag, u16 seq, int status,
|
||||
void *buf, size_t len),
|
||||
TP_ARGS(id, channel_id, protocol_id, msg_id, tag, seq, status,
|
||||
buf, len),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(int, id)
|
||||
__field(u8, channel_id)
|
||||
__field(u8, protocol_id)
|
||||
__field(u8, msg_id)
|
||||
__array(char, tag, 5)
|
||||
|
|
@ -154,6 +158,8 @@ TRACE_EVENT(scmi_msg_dump,
|
|||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->id = id;
|
||||
__entry->channel_id = channel_id;
|
||||
__entry->protocol_id = protocol_id;
|
||||
__entry->msg_id = msg_id;
|
||||
strscpy(__entry->tag, tag, 5);
|
||||
|
|
@ -163,9 +169,9 @@ TRACE_EVENT(scmi_msg_dump,
|
|||
memcpy(__get_dynamic_array(cmd), buf, __entry->len);
|
||||
),
|
||||
|
||||
TP_printk("pt=%02X t=%s msg_id=%02X seq=%04X s=%d pyld=%s",
|
||||
__entry->protocol_id, __entry->tag, __entry->msg_id,
|
||||
__entry->seq, __entry->status,
|
||||
TP_printk("id=%d ch=%02X pt=%02X t=%s msg_id=%02X seq=%04X s=%d pyld=%s",
|
||||
__entry->id, __entry->channel_id, __entry->protocol_id,
|
||||
__entry->tag, __entry->msg_id, __entry->seq, __entry->status,
|
||||
__print_hex_str(__get_dynamic_array(cmd), __entry->len))
|
||||
);
|
||||
#endif /* _TRACE_SCMI_H */
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user