RISC-V updates for the v6.18 merge window (part two)

Second set of RISC-V updates for the v6.18 merge window, consisting
 of:
 
 - Support for the RISC-V-standardized RPMI interface.
 
   RPMI is a platform management communication mechanism between OSes
   running on application processors, and a remote platform management
   processor.  Similar to ARM SCMI, TI SCI, etc.  This includes irqchip,
   mailbox, and clk changes.
 
 - Support for the RISC-V-standardized MPXY SBI extension.
 
   MPXY is a RISC-V-specific standard implementing a shared memory
   mailbox between S-mode operating systems (e.g., Linux) and M-mode
   firmware (e.g., OpenSBI).  It is part of this PR since one of its
   use cases is to enable M-mode firmware to act as a single RPMI client
   for all RPMI activity on a core (including S-mode RPMI activity).
   Includes a mailbox driver.
 
 - Some ACPI-related updates to enable the use of RPMI and MPXY.
 
 - The addition of Linux-wide memcpy_{from,to}_le32() static inline
   functions, for RPMI use.
 
 - An ACPI Kconfig change to enable boot logos on any ACPI-using
   architecture (including RISC-V)
 
 - A RISC-V defconfig change to add GPIO keyboard and event device
   support, for front panel shutdown or reboot buttons
 
 This PR also includes a recent, one-line Kconfig patch from Geert to
 keep non-RISC-V users from being asked about building the RPMI virtual
 clock driver when !COMPILE_TEST.  THere's nothing preventing
 non-RISC-V SoCs from implementing RPMI, but until some users show up,
 let's not annoy others with it.
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEElRDoIDdEz9/svf2Kx4+xDQu9KksFAmjfNccACgkQx4+xDQu9
 KkugVBAAsa9Oc6iEV1rwj1clWDrvz/bFJRxr/oH3PX0yJ4JJcZaBTydeW3fyJTJ8
 CzmRClGJ18YWjapJXYhMp1AS/VfsULu2AVfUzZUqHTjVZyxgt8xFgygpI+BHIyUN
 vD26iKJz/JvnytRmUi7mMtS0O48nTzdMiiOmu4Ved68YMJCRJKFZw8+rWVcAzrwb
 FHZlIE5Fcb1PRaUDg/45Baj0nEr+NRGKDLsR1rbocbmCmRMnz3ufPTcXk128+3gC
 VB1rQplcMBf2RpCl7p4LW2N746hcbg/RogfpjFy7KLlnEH+Xoh2nCxcWHaiEgR9q
 6JPsYBeekA54ZZsdoNBg1i5rGk3j/G1XGaV1bo7HDLTvShSByhaYrhAedQZEbw//
 xC3Eb7EQ6rNYUUjXiX0y5nhvl+nVlu/FmcsZmcP30ppOV4MQasTZ0zqfso23xhjL
 2e06PwTqsmXDeDNDQ4ruBKrpu8tkA7ZZvjCMq1rvSWjTPObzuGBe/ENrdBUOBb2E
 6UUeAGCZpQm1IxTcKHHxaIDT5ami745kqaBrXanIMKPX1JdCs7ahUqqWzC0LEgSy
 qB/T12bYg5O/yKXdXJuAuTHFb3TOPn6l8aNxRJve+uFwv4r1XXptdal9Yg2xoBWo
 EoGktm8KAp5Ndn5BntXI4xG4Ia3HOsj9YA7y4Iep4EO94JZk3Fk=
 =Ys1m
 -----END PGP SIGNATURE-----

Merge tag 'riscv-for-linus-6.18-mw2' of git://git.kernel.org/pub/scm/linux/kernel/git/riscv/linux

Pull more RISC-V updates from Paul Walmsley:

 - Support for the RISC-V-standardized RPMI interface.

   RPMI is a platform management communication mechanism between OSes
   running on application processors, and a remote platform management
   processor. Similar to ARM SCMI, TI SCI, etc. This includes irqchip,
   mailbox, and clk changes.

 - Support for the RISC-V-standardized MPXY SBI extension.

   MPXY is a RISC-V-specific standard implementing a shared memory
   mailbox between S-mode operating systems (e.g., Linux) and M-mode
   firmware (e.g., OpenSBI). It is part of this PR since one of its use
   cases is to enable M-mode firmware to act as a single RPMI client for
   all RPMI activity on a core (including S-mode RPMI activity).
   Includes a mailbox driver.

 - Some ACPI-related updates to enable the use of RPMI and MPXY.

 - The addition of Linux-wide memcpy_{from,to}_le32() static inline
   functions, for RPMI use.

 - An ACPI Kconfig change to enable boot logos on any ACPI-using
   architecture (including RISC-V)

 - A RISC-V defconfig change to add GPIO keyboard and event device
   support, for front panel shutdown or reboot buttons

* tag 'riscv-for-linus-6.18-mw2' of git://git.kernel.org/pub/scm/linux/kernel/git/riscv/linux: (26 commits)
  clk: COMMON_CLK_RPMI should depend on RISCV
  ACPI: support BGRT table on RISC-V
  MAINTAINERS: Add entry for RISC-V RPMI and MPXY drivers
  RISC-V: Enable GPIO keyboard and event device in RV64 defconfig
  irqchip/riscv-rpmi-sysmsi: Add ACPI support
  mailbox/riscv-sbi-mpxy: Add ACPI support
  irqchip/irq-riscv-imsic-early: Export imsic_acpi_get_fwnode()
  ACPI: RISC-V: Add RPMI System MSI to GSI mapping
  ACPI: RISC-V: Add support to update gsi range
  ACPI: RISC-V: Create interrupt controller list in sorted order
  ACPI: scan: Update honor list for RPMI System MSI
  ACPI: Add support for nargs_prop in acpi_fwnode_get_reference_args()
  ACPI: property: Refactor acpi_fwnode_get_reference_args() to support nargs_prop
  irqchip: Add driver for the RPMI system MSI service group
  dt-bindings: Add RPMI system MSI interrupt controller bindings
  dt-bindings: Add RPMI system MSI message proxy bindings
  clk: Add clock driver for the RISC-V RPMI clock service group
  dt-bindings: clock: Add RPMI clock service controller bindings
  dt-bindings: clock: Add RPMI clock service message proxy bindings
  mailbox: Add RISC-V SBI message proxy (MPXY) based mailbox driver
  ...
This commit is contained in:
Linus Torvalds 2025-10-04 10:36:22 -07:00
commit 86bcf7be1e
29 changed files with 2981 additions and 84 deletions

View File

@ -0,0 +1,64 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/clock/riscv,rpmi-clock.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: RISC-V RPMI clock service group based clock controller
maintainers:
- Anup Patel <anup@brainfault.org>
description: |
The RISC-V Platform Management Interface (RPMI) [1] defines a
messaging protocol which is modular and extensible. The supervisor
software can send/receive RPMI messages via SBI MPXY extension [2]
or some dedicated supervisor-mode RPMI transport.
The RPMI specification [1] defines clock service group for accessing
system clocks managed by a platform microcontroller. The supervisor
software can access RPMI clock service group via SBI MPXY channel or
some dedicated supervisor-mode RPMI transport.
===========================================
References
===========================================
[1] RISC-V Platform Management Interface (RPMI) v1.0 (or higher)
https://github.com/riscv-non-isa/riscv-rpmi/releases
[2] RISC-V Supervisor Binary Interface (SBI) v3.0 (or higher)
https://github.com/riscv-non-isa/riscv-sbi-doc/releases
properties:
compatible:
description:
Intended for use by the supervisor software.
const: riscv,rpmi-clock
mboxes:
maxItems: 1
description:
Mailbox channel of the underlying RPMI transport or SBI message proxy channel.
"#clock-cells":
const: 1
description:
Platform specific CLOCK_ID as defined by the RISC-V Platform Management
Interface (RPMI) specification.
required:
- compatible
- mboxes
- "#clock-cells"
additionalProperties: false
examples:
- |
clock-controller {
compatible = "riscv,rpmi-clock";
mboxes = <&mpxy_mbox 0x1000 0x0>;
#clock-cells = <1>;
};
...

View File

@ -0,0 +1,64 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/clock/riscv,rpmi-mpxy-clock.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: RISC-V RPMI clock service group based message proxy
maintainers:
- Anup Patel <anup@brainfault.org>
description: |
The RISC-V Platform Management Interface (RPMI) [1] defines a
messaging protocol which is modular and extensible. The supervisor
software can send/receive RPMI messages via SBI MPXY extension [2]
or some dedicated supervisor-mode RPMI transport.
The RPMI specification [1] defines clock service group for accessing
system clocks managed by a platform microcontroller. The SBI implementation
(machine mode firmware or hypervisor) can implement an SBI MPXY channel
to allow RPMI clock service group access to the supervisor software.
===========================================
References
===========================================
[1] RISC-V Platform Management Interface (RPMI) v1.0 (or higher)
https://github.com/riscv-non-isa/riscv-rpmi/releases
[2] RISC-V Supervisor Binary Interface (SBI) v3.0 (or higher)
https://github.com/riscv-non-isa/riscv-sbi-doc/releases
properties:
compatible:
description:
Intended for use by the SBI implementation.
const: riscv,rpmi-mpxy-clock
mboxes:
maxItems: 1
description:
Mailbox channel of the underlying RPMI transport.
riscv,sbi-mpxy-channel-id:
$ref: /schemas/types.yaml#/definitions/uint32
description:
The SBI MPXY channel id to be used for providing RPMI access to
the supervisor software.
required:
- compatible
- mboxes
- riscv,sbi-mpxy-channel-id
additionalProperties: false
examples:
- |
clock-service {
compatible = "riscv,rpmi-mpxy-clock";
mboxes = <&rpmi_shmem_mbox 0x8>;
riscv,sbi-mpxy-channel-id = <0x1000>;
};
...

View File

@ -0,0 +1,67 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/interrupt-controller/riscv,rpmi-mpxy-system-msi.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: RISC-V RPMI system MSI service group based message proxy
maintainers:
- Anup Patel <anup@brainfault.org>
description: |
The RISC-V Platform Management Interface (RPMI) [1] defines a
messaging protocol which is modular and extensible. The supervisor
software can send/receive RPMI messages via SBI MPXY extension [2]
or some dedicated supervisor-mode RPMI transport.
The RPMI specification [1] defines system MSI service group which
allow application processors to receive MSIs upon system events
such as P2A doorbell, graceful shutdown/reboot request, CPU hotplug
event, memory hotplug event, etc from the platform microcontroller.
The SBI implementation (machine mode firmware or hypervisor) can
implement an SBI MPXY channel to allow RPMI system MSI service
group access to the supervisor software.
===========================================
References
===========================================
[1] RISC-V Platform Management Interface (RPMI) v1.0 (or higher)
https://github.com/riscv-non-isa/riscv-rpmi/releases
[2] RISC-V Supervisor Binary Interface (SBI) v3.0 (or higher)
https://github.com/riscv-non-isa/riscv-sbi-doc/releases
properties:
compatible:
description:
Intended for use by the SBI implementation.
const: riscv,rpmi-mpxy-system-msi
mboxes:
maxItems: 1
description:
Mailbox channel of the underlying RPMI transport.
riscv,sbi-mpxy-channel-id:
$ref: /schemas/types.yaml#/definitions/uint32
description:
The SBI MPXY channel id to be used for providing RPMI access to
the supervisor software.
required:
- compatible
- mboxes
- riscv,sbi-mpxy-channel-id
additionalProperties: false
examples:
- |
interrupt-controller {
compatible = "riscv,rpmi-mpxy-system-msi";
mboxes = <&rpmi_shmem_mbox 0x2>;
riscv,sbi-mpxy-channel-id = <0x2000>;
};
...

View File

@ -0,0 +1,74 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/interrupt-controller/riscv,rpmi-system-msi.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: RISC-V RPMI system MSI service group based interrupt controller
maintainers:
- Anup Patel <anup@brainfault.org>
description: |
The RISC-V Platform Management Interface (RPMI) [1] defines a
messaging protocol which is modular and extensible. The supervisor
software can send/receive RPMI messages via SBI MPXY extension [2]
or some dedicated supervisor-mode RPMI transport.
The RPMI specification [1] defines system MSI service group which
allow application processors to receive MSIs upon system events
such as P2A doorbell, graceful shutdown/reboot request, CPU hotplug
event, memory hotplug event, etc from the platform microcontroller.
The supervisor software can access RPMI system MSI service group via
SBI MPXY channel or some dedicated supervisor-mode RPMI transport.
===========================================
References
===========================================
[1] RISC-V Platform Management Interface (RPMI) v1.0 (or higher)
https://github.com/riscv-non-isa/riscv-rpmi/releases
[2] RISC-V Supervisor Binary Interface (SBI) v3.0 (or higher)
https://github.com/riscv-non-isa/riscv-sbi-doc/releases
allOf:
- $ref: /schemas/interrupt-controller.yaml#
properties:
compatible:
description:
Intended for use by the supervisor software.
const: riscv,rpmi-system-msi
mboxes:
maxItems: 1
description:
Mailbox channel of the underlying RPMI transport or SBI message proxy channel.
msi-parent: true
interrupt-controller: true
"#interrupt-cells":
const: 1
required:
- compatible
- mboxes
- msi-parent
- interrupt-controller
- "#interrupt-cells"
additionalProperties: false
examples:
- |
interrupt-controller {
compatible = "riscv,rpmi-system-msi";
mboxes = <&mpxy_mbox 0x2000 0x0>;
msi-parent = <&imsic_slevel>;
interrupt-controller;
#interrupt-cells = <1>;
};
...

View File

@ -0,0 +1,124 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/mailbox/riscv,rpmi-shmem-mbox.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: RISC-V Platform Management Interface (RPMI) shared memory mailbox
maintainers:
- Anup Patel <anup@brainfault.org>
description: |
The RISC-V Platform Management Interface (RPMI) [1] defines a common shared
memory based RPMI transport. This RPMI shared memory transport integrates as
mailbox controller in the SBI implementation or supervisor software whereas
each RPMI service group is mailbox client in the SBI implementation and
supervisor software.
===========================================
References
===========================================
[1] RISC-V Platform Management Interface (RPMI) v1.0 (or higher)
https://github.com/riscv-non-isa/riscv-rpmi/releases
properties:
compatible:
const: riscv,rpmi-shmem-mbox
reg:
minItems: 2
items:
- description: A2P request queue base address
- description: P2A acknowledgment queue base address
- description: P2A request queue base address
- description: A2P acknowledgment queue base address
- description: A2P doorbell address
reg-names:
minItems: 2
items:
- const: a2p-req
- const: p2a-ack
- enum: [ p2a-req, a2p-doorbell ]
- const: a2p-ack
- const: a2p-doorbell
interrupts:
maxItems: 1
description:
The RPMI shared memory transport supports P2A doorbell as a wired
interrupt and this property specifies the interrupt source.
msi-parent:
description:
The RPMI shared memory transport supports P2A doorbell as a system MSI
and this property specifies the target MSI controller.
riscv,slot-size:
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 64
description:
Power-of-2 RPMI slot size of the RPMI shared memory transport.
riscv,a2p-doorbell-value:
$ref: /schemas/types.yaml#/definitions/uint32
default: 0x1
description:
Value written to the 32-bit A2P doorbell register.
riscv,p2a-doorbell-sysmsi-index:
$ref: /schemas/types.yaml#/definitions/uint32
description:
The RPMI shared memory transport supports P2A doorbell as a system MSI
and this property specifies system MSI index to be used for configuring
the P2A doorbell MSI.
"#mbox-cells":
const: 1
description:
The first cell specifies RPMI service group ID.
required:
- compatible
- reg
- reg-names
- riscv,slot-size
- "#mbox-cells"
anyOf:
- required:
- interrupts
- required:
- msi-parent
additionalProperties: false
examples:
- |
// Example 1 (RPMI shared memory with only 2 queues):
mailbox@10080000 {
compatible = "riscv,rpmi-shmem-mbox";
reg = <0x10080000 0x10000>,
<0x10090000 0x10000>;
reg-names = "a2p-req", "p2a-ack";
msi-parent = <&imsic_mlevel>;
riscv,slot-size = <64>;
#mbox-cells = <1>;
};
- |
// Example 2 (RPMI shared memory with only 4 queues):
mailbox@10001000 {
compatible = "riscv,rpmi-shmem-mbox";
reg = <0x10001000 0x800>,
<0x10001800 0x800>,
<0x10002000 0x800>,
<0x10002800 0x800>,
<0x10003000 0x4>;
reg-names = "a2p-req", "p2a-ack", "p2a-req", "a2p-ack", "a2p-doorbell";
msi-parent = <&imsic_mlevel>;
riscv,slot-size = <64>;
riscv,a2p-doorbell-value = <0x00008000>;
#mbox-cells = <1>;
};

View File

@ -0,0 +1,51 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/mailbox/riscv,sbi-mpxy-mbox.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: RISC-V SBI Message Proxy (MPXY) extension based mailbox
maintainers:
- Anup Patel <anup@brainfault.org>
description: |
The RISC-V SBI Message Proxy (MPXY) extension [1] allows supervisor
software to send messages through the SBI implementation (M-mode
firmware or HS-mode hypervisor). The underlying message protocol
and message format used by the supervisor software could be some
other standard protocol compatible with the SBI MPXY extension
(such as RISC-V Platform Management Interface (RPMI) [2]).
===========================================
References
===========================================
[1] RISC-V Supervisor Binary Interface (SBI) v3.0 (or higher)
https://github.com/riscv-non-isa/riscv-sbi-doc/releases
[2] RISC-V Platform Management Interface (RPMI) v1.0 (or higher)
https://github.com/riscv-non-isa/riscv-rpmi/releases
properties:
compatible:
const: riscv,sbi-mpxy-mbox
"#mbox-cells":
const: 2
description:
The first cell specifies channel_id of the SBI MPXY channel,
the second cell specifies MSG_PROT_ID of the SBI MPXY channel
required:
- compatible
- "#mbox-cells"
additionalProperties: false
examples:
- |
mailbox {
compatible = "riscv,sbi-mpxy-mbox";
#mbox-cells = <2>;
};

View File

@ -22066,6 +22066,21 @@ F: drivers/perf/riscv_pmu.c
F: drivers/perf/riscv_pmu_legacy.c F: drivers/perf/riscv_pmu_legacy.c
F: drivers/perf/riscv_pmu_sbi.c F: drivers/perf/riscv_pmu_sbi.c
RISC-V RPMI AND MPXY DRIVERS
M: Rahul Pathak <rahul@summations.net>
M: Anup Patel <anup@brainfault.org>
L: linux-riscv@lists.infradead.org
F: Documentation/devicetree/bindings/clock/riscv,rpmi-clock.yaml
F: Documentation/devicetree/bindings/clock/riscv,rpmi-mpxy-clock.yaml
F: Documentation/devicetree/bindings/interrupt-controller/riscv,rpmi-mpxy-system-msi.yaml
F: Documentation/devicetree/bindings/interrupt-controller/riscv,rpmi-system-msi.yaml
F: Documentation/devicetree/bindings/mailbox/riscv,rpmi-shmem-mbox.yaml
F: Documentation/devicetree/bindings/mailbox/riscv,sbi-mpxy-mbox.yaml
F: drivers/clk/clk-rpmi.c
F: drivers/irqchip/irq-riscv-rpmi-sysmsi.c
F: drivers/mailbox/riscv-sbi-mpxy-mbox.c
F: include/linux/mailbox/riscv-rpmi-message.h
RISC-V SPACEMIT SoC Support RISC-V SPACEMIT SoC Support
M: Yixun Lan <dlan@gentoo.org> M: Yixun Lan <dlan@gentoo.org>
L: linux-riscv@lists.infradead.org L: linux-riscv@lists.infradead.org

View File

@ -140,6 +140,8 @@ CONFIG_MICREL_PHY=y
CONFIG_MICROSEMI_PHY=y CONFIG_MICROSEMI_PHY=y
CONFIG_MOTORCOMM_PHY=y CONFIG_MOTORCOMM_PHY=y
CONFIG_INPUT_MOUSEDEV=y CONFIG_INPUT_MOUSEDEV=y
CONFIG_INPUT_EVDEV=y
CONFIG_KEYBOARD_GPIO=y
CONFIG_KEYBOARD_SUN4I_LRADC=m CONFIG_KEYBOARD_SUN4I_LRADC=m
CONFIG_SERIAL_8250=y CONFIG_SERIAL_8250=y
CONFIG_SERIAL_8250_CONSOLE=y CONFIG_SERIAL_8250_CONSOLE=y

View File

@ -32,6 +32,7 @@ enum riscv_irqchip_type {
ACPI_RISCV_IRQCHIP_IMSIC = 0x01, ACPI_RISCV_IRQCHIP_IMSIC = 0x01,
ACPI_RISCV_IRQCHIP_PLIC = 0x02, ACPI_RISCV_IRQCHIP_PLIC = 0x02,
ACPI_RISCV_IRQCHIP_APLIC = 0x03, ACPI_RISCV_IRQCHIP_APLIC = 0x03,
ACPI_RISCV_IRQCHIP_SMSI = 0x04,
}; };
int riscv_acpi_get_gsi_info(struct fwnode_handle *fwnode, u32 *gsi_base, int riscv_acpi_get_gsi_info(struct fwnode_handle *fwnode, u32 *gsi_base,
@ -42,6 +43,7 @@ unsigned long acpi_rintc_ext_parent_to_hartid(unsigned int plic_id, unsigned int
unsigned int acpi_rintc_get_plic_nr_contexts(unsigned int plic_id); unsigned int acpi_rintc_get_plic_nr_contexts(unsigned int plic_id);
unsigned int acpi_rintc_get_plic_context(unsigned int plic_id, unsigned int ctxt_idx); unsigned int acpi_rintc_get_plic_context(unsigned int plic_id, unsigned int ctxt_idx);
int __init acpi_rintc_get_imsic_mmio_info(u32 index, struct resource *res); int __init acpi_rintc_get_imsic_mmio_info(u32 index, struct resource *res);
int riscv_acpi_update_gsi_range(u32 gsi_base, u32 nr_irqs);
#else #else
static inline int riscv_acpi_get_gsi_info(struct fwnode_handle *fwnode, u32 *gsi_base, static inline int riscv_acpi_get_gsi_info(struct fwnode_handle *fwnode, u32 *gsi_base,
@ -76,6 +78,10 @@ static inline int __init acpi_rintc_get_imsic_mmio_info(u32 index, struct resour
return 0; return 0;
} }
static inline int riscv_acpi_update_gsi_range(u32 gsi_base, u32 nr_irqs)
{
return -ENODEV;
}
#endif /* CONFIG_ACPI */ #endif /* CONFIG_ACPI */
#endif /* _ASM_RISCV_IRQ_H */ #endif /* _ASM_RISCV_IRQ_H */

View File

@ -36,6 +36,7 @@ enum sbi_ext_id {
SBI_EXT_STA = 0x535441, SBI_EXT_STA = 0x535441,
SBI_EXT_NACL = 0x4E41434C, SBI_EXT_NACL = 0x4E41434C,
SBI_EXT_FWFT = 0x46574654, SBI_EXT_FWFT = 0x46574654,
SBI_EXT_MPXY = 0x4D505859,
/* Experimentals extensions must lie within this range */ /* Experimentals extensions must lie within this range */
SBI_EXT_EXPERIMENTAL_START = 0x08000000, SBI_EXT_EXPERIMENTAL_START = 0x08000000,
@ -443,6 +444,67 @@ enum sbi_fwft_feature_t {
#define SBI_FWFT_SET_FLAG_LOCK BIT(0) #define SBI_FWFT_SET_FLAG_LOCK BIT(0)
enum sbi_ext_mpxy_fid {
SBI_EXT_MPXY_GET_SHMEM_SIZE,
SBI_EXT_MPXY_SET_SHMEM,
SBI_EXT_MPXY_GET_CHANNEL_IDS,
SBI_EXT_MPXY_READ_ATTRS,
SBI_EXT_MPXY_WRITE_ATTRS,
SBI_EXT_MPXY_SEND_MSG_WITH_RESP,
SBI_EXT_MPXY_SEND_MSG_WITHOUT_RESP,
SBI_EXT_MPXY_GET_NOTIFICATION_EVENTS,
};
enum sbi_mpxy_attribute_id {
/* Standard channel attributes managed by MPXY framework */
SBI_MPXY_ATTR_MSG_PROT_ID = 0x00000000,
SBI_MPXY_ATTR_MSG_PROT_VER = 0x00000001,
SBI_MPXY_ATTR_MSG_MAX_LEN = 0x00000002,
SBI_MPXY_ATTR_MSG_SEND_TIMEOUT = 0x00000003,
SBI_MPXY_ATTR_MSG_COMPLETION_TIMEOUT = 0x00000004,
SBI_MPXY_ATTR_CHANNEL_CAPABILITY = 0x00000005,
SBI_MPXY_ATTR_SSE_EVENT_ID = 0x00000006,
SBI_MPXY_ATTR_MSI_CONTROL = 0x00000007,
SBI_MPXY_ATTR_MSI_ADDR_LO = 0x00000008,
SBI_MPXY_ATTR_MSI_ADDR_HI = 0x00000009,
SBI_MPXY_ATTR_MSI_DATA = 0x0000000A,
SBI_MPXY_ATTR_EVENTS_STATE_CONTROL = 0x0000000B,
SBI_MPXY_ATTR_STD_ATTR_MAX_IDX,
/*
* Message protocol specific attributes, managed by
* the message protocol specification.
*/
SBI_MPXY_ATTR_MSGPROTO_ATTR_START = 0x80000000,
SBI_MPXY_ATTR_MSGPROTO_ATTR_END = 0xffffffff
};
/* Possible values of MSG_PROT_ID attribute as-per SBI v3.0 (or higher) */
enum sbi_mpxy_msgproto_id {
SBI_MPXY_MSGPROTO_RPMI_ID = 0x0,
};
/* RPMI message protocol specific MPXY attributes */
enum sbi_mpxy_rpmi_attribute_id {
SBI_MPXY_RPMI_ATTR_SERVICEGROUP_ID = SBI_MPXY_ATTR_MSGPROTO_ATTR_START,
SBI_MPXY_RPMI_ATTR_SERVICEGROUP_VERSION,
SBI_MPXY_RPMI_ATTR_IMPL_ID,
SBI_MPXY_RPMI_ATTR_IMPL_VERSION,
SBI_MPXY_RPMI_ATTR_MAX_ID
};
/* Encoding of MSG_PROT_VER attribute */
#define SBI_MPXY_MSG_PROT_VER_MAJOR(__ver) upper_16_bits(__ver)
#define SBI_MPXY_MSG_PROT_VER_MINOR(__ver) lower_16_bits(__ver)
#define SBI_MPXY_MSG_PROT_MKVER(__maj, __min) (((u32)(__maj) << 16) | (u16)(__min))
/* Capabilities available through CHANNEL_CAPABILITY attribute */
#define SBI_MPXY_CHAN_CAP_MSI BIT(0)
#define SBI_MPXY_CHAN_CAP_SSE BIT(1)
#define SBI_MPXY_CHAN_CAP_EVENTS_STATE BIT(2)
#define SBI_MPXY_CHAN_CAP_SEND_WITH_RESP BIT(3)
#define SBI_MPXY_CHAN_CAP_SEND_WITHOUT_RESP BIT(4)
#define SBI_MPXY_CHAN_CAP_GET_NOTIFICATIONS BIT(5)
/* SBI spec version fields */ /* SBI spec version fields */
#define SBI_SPEC_VERSION_DEFAULT 0x1 #define SBI_SPEC_VERSION_DEFAULT 0x1
#define SBI_SPEC_VERSION_MAJOR_SHIFT 24 #define SBI_SPEC_VERSION_MAJOR_SHIFT 24

View File

@ -461,7 +461,7 @@ config ACPI_HED
config ACPI_BGRT config ACPI_BGRT
bool "Boottime Graphics Resource Table support" bool "Boottime Graphics Resource Table support"
depends on EFI && (X86 || ARM64 || LOONGARCH) depends on EFI
help help
This driver adds support for exposing the ACPI Boottime Graphics This driver adds support for exposing the ACPI Boottime Graphics
Resource Table, which allows the operating system to obtain Resource Table, which allows the operating system to obtain

View File

@ -844,13 +844,35 @@ acpi_fwnode_get_named_child_node(const struct fwnode_handle *fwnode,
return NULL; return NULL;
} }
static unsigned int acpi_fwnode_get_args_count(struct fwnode_handle *fwnode,
const char *nargs_prop)
{
const struct acpi_device_data *data;
const union acpi_object *obj;
int ret;
data = acpi_device_data_of_node(fwnode);
if (!data)
return 0;
ret = acpi_data_get_property(data, nargs_prop, ACPI_TYPE_INTEGER, &obj);
if (ret)
return 0;
return obj->integer.value;
}
static int acpi_get_ref_args(struct fwnode_reference_args *args, static int acpi_get_ref_args(struct fwnode_reference_args *args,
struct fwnode_handle *ref_fwnode, struct fwnode_handle *ref_fwnode,
const char *nargs_prop,
const union acpi_object **element, const union acpi_object **element,
const union acpi_object *end, size_t num_args) const union acpi_object *end, size_t num_args)
{ {
u32 nargs = 0, i; u32 nargs = 0, i;
if (nargs_prop)
num_args = acpi_fwnode_get_args_count(ref_fwnode, nargs_prop);
/* /*
* Assume the following integer elements are all args. Stop counting on * Assume the following integer elements are all args. Stop counting on
* the first reference (possibly represented as a string) or end of the * the first reference (possibly represented as a string) or end of the
@ -922,45 +944,10 @@ static struct fwnode_handle *acpi_parse_string_ref(const struct fwnode_handle *f
return &dn->fwnode; return &dn->fwnode;
} }
/** static int acpi_fwnode_get_reference_args(const struct fwnode_handle *fwnode,
* __acpi_node_get_property_reference - returns handle to the referenced object const char *propname, const char *nargs_prop,
* @fwnode: Firmware node to get the property from unsigned int args_count, unsigned int index,
* @propname: Name of the property struct fwnode_reference_args *args)
* @index: Index of the reference to return
* @num_args: Maximum number of arguments after each reference
* @args: Location to store the returned reference with optional arguments
* (may be NULL)
*
* Find property with @name, verifify that it is a package containing at least
* one object reference and if so, store the ACPI device object pointer to the
* target object in @args->adev. If the reference includes arguments, store
* them in the @args->args[] array.
*
* If there's more than one reference in the property value package, @index is
* used to select the one to return.
*
* It is possible to leave holes in the property value set like in the
* example below:
*
* Package () {
* "cs-gpios",
* Package () {
* ^GPIO, 19, 0, 0,
* ^GPIO, 20, 0, 0,
* 0,
* ^GPIO, 21, 0, 0,
* }
* }
*
* Calling this function with index %2 or index %3 return %-ENOENT. If the
* property does not contain any more values %-ENOENT is returned. The NULL
* entry must be single integer and preferably contain value %0.
*
* Return: %0 on success, negative error code on failure.
*/
int __acpi_node_get_property_reference(const struct fwnode_handle *fwnode,
const char *propname, size_t index, size_t num_args,
struct fwnode_reference_args *args)
{ {
const union acpi_object *element, *end; const union acpi_object *element, *end;
const union acpi_object *obj; const union acpi_object *obj;
@ -1036,10 +1023,10 @@ int __acpi_node_get_property_reference(const struct fwnode_handle *fwnode,
return -EINVAL; return -EINVAL;
element++; element++;
ret = acpi_get_ref_args(idx == index ? args : NULL, ret = acpi_get_ref_args(idx == index ? args : NULL,
acpi_fwnode_handle(device), acpi_fwnode_handle(device),
&element, end, num_args); nargs_prop, &element, end,
args_count);
if (ret < 0) if (ret < 0)
return ret; return ret;
@ -1054,10 +1041,9 @@ int __acpi_node_get_property_reference(const struct fwnode_handle *fwnode,
return -EINVAL; return -EINVAL;
element++; element++;
ret = acpi_get_ref_args(idx == index ? args : NULL, ret = acpi_get_ref_args(idx == index ? args : NULL,
ref_fwnode, &element, end, ref_fwnode, nargs_prop, &element, end,
num_args); args_count);
if (ret < 0) if (ret < 0)
return ret; return ret;
@ -1079,6 +1065,50 @@ int __acpi_node_get_property_reference(const struct fwnode_handle *fwnode,
return -ENOENT; return -ENOENT;
} }
/**
* __acpi_node_get_property_reference - returns handle to the referenced object
* @fwnode: Firmware node to get the property from
* @propname: Name of the property
* @index: Index of the reference to return
* @num_args: Maximum number of arguments after each reference
* @args: Location to store the returned reference with optional arguments
* (may be NULL)
*
* Find property with @name, verifify that it is a package containing at least
* one object reference and if so, store the ACPI device object pointer to the
* target object in @args->adev. If the reference includes arguments, store
* them in the @args->args[] array.
*
* If there's more than one reference in the property value package, @index is
* used to select the one to return.
*
* It is possible to leave holes in the property value set like in the
* example below:
*
* Package () {
* "cs-gpios",
* Package () {
* ^GPIO, 19, 0, 0,
* ^GPIO, 20, 0, 0,
* 0,
* ^GPIO, 21, 0, 0,
* }
* }
*
* Calling this function with index %2 or index %3 return %-ENOENT. If the
* property does not contain any more values %-ENOENT is returned. The NULL
* entry must be single integer and preferably contain value %0.
*
* Return: %0 on success, negative error code on failure.
*/
int __acpi_node_get_property_reference(const struct fwnode_handle *fwnode,
const char *propname, size_t index,
size_t num_args,
struct fwnode_reference_args *args)
{
return acpi_fwnode_get_reference_args(fwnode, propname, NULL, index, num_args, args);
}
EXPORT_SYMBOL_GPL(__acpi_node_get_property_reference); EXPORT_SYMBOL_GPL(__acpi_node_get_property_reference);
static int acpi_data_prop_read_single(const struct acpi_device_data *data, static int acpi_data_prop_read_single(const struct acpi_device_data *data,
@ -1598,16 +1628,6 @@ acpi_fwnode_property_read_string_array(const struct fwnode_handle *fwnode,
val, nval); val, nval);
} }
static int
acpi_fwnode_get_reference_args(const struct fwnode_handle *fwnode,
const char *prop, const char *nargs_prop,
unsigned int args_count, unsigned int index,
struct fwnode_reference_args *args)
{
return __acpi_node_get_property_reference(fwnode, prop, index,
args_count, args);
}
static const char *acpi_fwnode_get_name(const struct fwnode_handle *fwnode) static const char *acpi_fwnode_get_name(const struct fwnode_handle *fwnode)
{ {
const struct acpi_device *adev; const struct acpi_device *adev;

View File

@ -10,6 +10,8 @@
#include "init.h" #include "init.h"
#define RISCV_ACPI_INTC_FLAG_PENDING BIT(0)
struct riscv_ext_intc_list { struct riscv_ext_intc_list {
acpi_handle handle; acpi_handle handle;
u32 gsi_base; u32 gsi_base;
@ -17,6 +19,7 @@ struct riscv_ext_intc_list {
u32 nr_idcs; u32 nr_idcs;
u32 id; u32 id;
u32 type; u32 type;
u32 flag;
struct list_head list; struct list_head list;
}; };
@ -69,6 +72,22 @@ static acpi_status riscv_acpi_update_gsi_handle(u32 gsi_base, acpi_handle handle
return AE_NOT_FOUND; return AE_NOT_FOUND;
} }
int riscv_acpi_update_gsi_range(u32 gsi_base, u32 nr_irqs)
{
struct riscv_ext_intc_list *ext_intc_element;
list_for_each_entry(ext_intc_element, &ext_intc_list, list) {
if (gsi_base == ext_intc_element->gsi_base &&
(ext_intc_element->flag & RISCV_ACPI_INTC_FLAG_PENDING)) {
ext_intc_element->nr_irqs = nr_irqs;
ext_intc_element->flag &= ~RISCV_ACPI_INTC_FLAG_PENDING;
return 0;
}
}
return -ENODEV;
}
int riscv_acpi_get_gsi_info(struct fwnode_handle *fwnode, u32 *gsi_base, int riscv_acpi_get_gsi_info(struct fwnode_handle *fwnode, u32 *gsi_base,
u32 *id, u32 *nr_irqs, u32 *nr_idcs) u32 *id, u32 *nr_irqs, u32 *nr_idcs)
{ {
@ -115,20 +134,67 @@ struct fwnode_handle *riscv_acpi_get_gsi_domain_id(u32 gsi)
static int __init riscv_acpi_register_ext_intc(u32 gsi_base, u32 nr_irqs, u32 nr_idcs, static int __init riscv_acpi_register_ext_intc(u32 gsi_base, u32 nr_irqs, u32 nr_idcs,
u32 id, u32 type) u32 id, u32 type)
{ {
struct riscv_ext_intc_list *ext_intc_element; struct riscv_ext_intc_list *ext_intc_element, *node, *prev;
ext_intc_element = kzalloc(sizeof(*ext_intc_element), GFP_KERNEL); ext_intc_element = kzalloc(sizeof(*ext_intc_element), GFP_KERNEL);
if (!ext_intc_element) if (!ext_intc_element)
return -ENOMEM; return -ENOMEM;
ext_intc_element->gsi_base = gsi_base; ext_intc_element->gsi_base = gsi_base;
ext_intc_element->nr_irqs = nr_irqs;
/* If nr_irqs is zero, indicate it in flag and set to max range possible */
if (nr_irqs) {
ext_intc_element->nr_irqs = nr_irqs;
} else {
ext_intc_element->flag |= RISCV_ACPI_INTC_FLAG_PENDING;
ext_intc_element->nr_irqs = U32_MAX - ext_intc_element->gsi_base;
}
ext_intc_element->nr_idcs = nr_idcs; ext_intc_element->nr_idcs = nr_idcs;
ext_intc_element->id = id; ext_intc_element->id = id;
list_add_tail(&ext_intc_element->list, &ext_intc_list); list_for_each_entry(node, &ext_intc_list, list) {
if (node->gsi_base < ext_intc_element->gsi_base)
break;
}
/* Adjust the previous node's GSI range if that has pending registration */
prev = list_prev_entry(node, list);
if (!list_entry_is_head(prev, &ext_intc_list, list)) {
if (prev->flag & RISCV_ACPI_INTC_FLAG_PENDING)
prev->nr_irqs = ext_intc_element->gsi_base - prev->gsi_base;
}
list_add_tail(&ext_intc_element->list, &node->list);
return 0; return 0;
} }
static acpi_status __init riscv_acpi_create_gsi_map_smsi(acpi_handle handle, u32 level,
void *context, void **return_value)
{
acpi_status status;
u64 gbase;
if (!acpi_has_method(handle, "_GSB")) {
acpi_handle_err(handle, "_GSB method not found\n");
return AE_ERROR;
}
status = acpi_evaluate_integer(handle, "_GSB", NULL, &gbase);
if (ACPI_FAILURE(status)) {
acpi_handle_err(handle, "failed to evaluate _GSB method\n");
return status;
}
riscv_acpi_register_ext_intc(gbase, 0, 0, 0, ACPI_RISCV_IRQCHIP_SMSI);
status = riscv_acpi_update_gsi_handle((u32)gbase, handle);
if (ACPI_FAILURE(status)) {
acpi_handle_err(handle, "failed to find the GSI mapping entry\n");
return status;
}
return AE_OK;
}
static acpi_status __init riscv_acpi_create_gsi_map(acpi_handle handle, u32 level, static acpi_status __init riscv_acpi_create_gsi_map(acpi_handle handle, u32 level,
void *context, void **return_value) void *context, void **return_value)
{ {
@ -183,6 +249,9 @@ void __init riscv_acpi_init_gsi_mapping(void)
if (acpi_table_parse_madt(ACPI_MADT_TYPE_APLIC, riscv_acpi_aplic_parse_madt, 0) > 0) if (acpi_table_parse_madt(ACPI_MADT_TYPE_APLIC, riscv_acpi_aplic_parse_madt, 0) > 0)
acpi_get_devices("RSCV0002", riscv_acpi_create_gsi_map, NULL, NULL); acpi_get_devices("RSCV0002", riscv_acpi_create_gsi_map, NULL, NULL);
/* Unlike PLIC/APLIC, SYSMSI doesn't have MADT */
acpi_get_devices("RSCV0006", riscv_acpi_create_gsi_map_smsi, NULL, NULL);
} }
static acpi_handle riscv_acpi_get_gsi_handle(u32 gsi) static acpi_handle riscv_acpi_get_gsi_handle(u32 gsi)

View File

@ -861,6 +861,8 @@ static const char * const acpi_honor_dep_ids[] = {
"INTC10CF", /* IVSC (MTL) driver must be loaded to allow i2c access to camera sensors */ "INTC10CF", /* IVSC (MTL) driver must be loaded to allow i2c access to camera sensors */
"RSCV0001", /* RISC-V PLIC */ "RSCV0001", /* RISC-V PLIC */
"RSCV0002", /* RISC-V APLIC */ "RSCV0002", /* RISC-V APLIC */
"RSCV0005", /* RISC-V SBI MPXY MBOX */
"RSCV0006", /* RISC-V RPMI SYSMSI */
"PNP0C0F", /* PCI Link Device */ "PNP0C0F", /* PCI Link Device */
NULL NULL
}; };

View File

@ -578,7 +578,7 @@ EXPORT_SYMBOL_GPL(fwnode_property_match_property_string);
* @prop: The name of the property * @prop: The name of the property
* @nargs_prop: The name of the property telling the number of * @nargs_prop: The name of the property telling the number of
* arguments in the referred node. NULL if @nargs is known, * arguments in the referred node. NULL if @nargs is known,
* otherwise @nargs is ignored. Only relevant on OF. * otherwise @nargs is ignored.
* @nargs: Number of arguments. Ignored if @nargs_prop is non-NULL. * @nargs: Number of arguments. Ignored if @nargs_prop is non-NULL.
* @index: Index of the reference, from zero onwards. * @index: Index of the reference, from zero onwards.
* @args: Result structure with reference and integer arguments. * @args: Result structure with reference and integer arguments.

View File

@ -501,6 +501,15 @@ config COMMON_CLK_SP7021
Not all features of the PLL are currently supported Not all features of the PLL are currently supported
by the driver. by the driver.
config COMMON_CLK_RPMI
tristate "Clock driver based on RISC-V RPMI"
depends on RISCV || COMPILE_TEST
depends on MAILBOX
default RISCV
help
Support for clocks based on the clock service group defined by
the RISC-V platform management interface (RPMI) specification.
source "drivers/clk/actions/Kconfig" source "drivers/clk/actions/Kconfig"
source "drivers/clk/analogbits/Kconfig" source "drivers/clk/analogbits/Kconfig"
source "drivers/clk/baikal-t1/Kconfig" source "drivers/clk/baikal-t1/Kconfig"

View File

@ -86,6 +86,7 @@ obj-$(CONFIG_COMMON_CLK_PWM) += clk-pwm.o
obj-$(CONFIG_CLK_QORIQ) += clk-qoriq.o obj-$(CONFIG_CLK_QORIQ) += clk-qoriq.o
obj-$(CONFIG_COMMON_CLK_RK808) += clk-rk808.o obj-$(CONFIG_COMMON_CLK_RK808) += clk-rk808.o
obj-$(CONFIG_COMMON_CLK_RP1) += clk-rp1.o obj-$(CONFIG_COMMON_CLK_RP1) += clk-rp1.o
obj-$(CONFIG_COMMON_CLK_RPMI) += clk-rpmi.o
obj-$(CONFIG_COMMON_CLK_HI655X) += clk-hi655x.o obj-$(CONFIG_COMMON_CLK_HI655X) += clk-hi655x.o
obj-$(CONFIG_COMMON_CLK_S2MPS11) += clk-s2mps11.o obj-$(CONFIG_COMMON_CLK_S2MPS11) += clk-s2mps11.o
obj-$(CONFIG_COMMON_CLK_SCMI) += clk-scmi.o obj-$(CONFIG_COMMON_CLK_SCMI) += clk-scmi.o

620
drivers/clk/clk-rpmi.c Normal file
View File

@ -0,0 +1,620 @@
// SPDX-License-Identifier: GPL-2.0
/*
* RISC-V MPXY Based Clock Driver
*
* Copyright (C) 2025 Ventana Micro Systems Ltd.
*/
#include <linux/clk-provider.h>
#include <linux/err.h>
#include <linux/mailbox_client.h>
#include <linux/mailbox/riscv-rpmi-message.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/wordpart.h>
#define RPMI_CLK_DISCRETE_MAX_NUM_RATES 16
#define RPMI_CLK_NAME_LEN 16
#define to_rpmi_clk(clk) container_of(clk, struct rpmi_clk, hw)
enum rpmi_clk_config {
RPMI_CLK_DISABLE = 0,
RPMI_CLK_ENABLE = 1,
RPMI_CLK_CONFIG_MAX_IDX
};
#define RPMI_CLK_TYPE_MASK GENMASK(1, 0)
enum rpmi_clk_type {
RPMI_CLK_DISCRETE = 0,
RPMI_CLK_LINEAR = 1,
RPMI_CLK_TYPE_MAX_IDX
};
struct rpmi_clk_context {
struct device *dev;
struct mbox_chan *chan;
struct mbox_client client;
u32 max_msg_data_size;
};
/*
* rpmi_clk_rates represents the rates format
* as specified by the RPMI specification.
* No other data format (e.g., struct linear_range)
* is required to avoid to and from conversion.
*/
union rpmi_clk_rates {
u64 discrete[RPMI_CLK_DISCRETE_MAX_NUM_RATES];
struct {
u64 min;
u64 max;
u64 step;
} linear;
};
struct rpmi_clk {
struct rpmi_clk_context *context;
u32 id;
u32 num_rates;
u32 transition_latency;
enum rpmi_clk_type type;
union rpmi_clk_rates *rates;
char name[RPMI_CLK_NAME_LEN];
struct clk_hw hw;
};
struct rpmi_clk_rate_discrete {
__le32 lo;
__le32 hi;
};
struct rpmi_clk_rate_linear {
__le32 min_lo;
__le32 min_hi;
__le32 max_lo;
__le32 max_hi;
__le32 step_lo;
__le32 step_hi;
};
struct rpmi_get_num_clocks_rx {
__le32 status;
__le32 num_clocks;
};
struct rpmi_get_attrs_tx {
__le32 clkid;
};
struct rpmi_get_attrs_rx {
__le32 status;
__le32 flags;
__le32 num_rates;
__le32 transition_latency;
char name[RPMI_CLK_NAME_LEN];
};
struct rpmi_get_supp_rates_tx {
__le32 clkid;
__le32 clk_rate_idx;
};
struct rpmi_get_supp_rates_rx {
__le32 status;
__le32 flags;
__le32 remaining;
__le32 returned;
__le32 rates[];
};
struct rpmi_get_rate_tx {
__le32 clkid;
};
struct rpmi_get_rate_rx {
__le32 status;
__le32 lo;
__le32 hi;
};
struct rpmi_set_rate_tx {
__le32 clkid;
__le32 flags;
__le32 lo;
__le32 hi;
};
struct rpmi_set_rate_rx {
__le32 status;
};
struct rpmi_set_config_tx {
__le32 clkid;
__le32 config;
};
struct rpmi_set_config_rx {
__le32 status;
};
static inline u64 rpmi_clkrate_u64(u32 __hi, u32 __lo)
{
return (((u64)(__hi) << 32) | (u32)(__lo));
}
static u32 rpmi_clk_get_num_clocks(struct rpmi_clk_context *context)
{
struct rpmi_get_num_clocks_rx rx, *resp;
struct rpmi_mbox_message msg;
int ret;
rpmi_mbox_init_send_with_response(&msg, RPMI_CLK_SRV_GET_NUM_CLOCKS,
NULL, 0, &rx, sizeof(rx));
ret = rpmi_mbox_send_message(context->chan, &msg);
if (ret)
return 0;
resp = rpmi_mbox_get_msg_response(&msg);
if (!resp || resp->status)
return 0;
return le32_to_cpu(resp->num_clocks);
}
static int rpmi_clk_get_attrs(u32 clkid, struct rpmi_clk *rpmi_clk)
{
struct rpmi_clk_context *context = rpmi_clk->context;
struct rpmi_mbox_message msg;
struct rpmi_get_attrs_tx tx;
struct rpmi_get_attrs_rx rx, *resp;
u8 format;
int ret;
tx.clkid = cpu_to_le32(clkid);
rpmi_mbox_init_send_with_response(&msg, RPMI_CLK_SRV_GET_ATTRIBUTES,
&tx, sizeof(tx), &rx, sizeof(rx));
ret = rpmi_mbox_send_message(context->chan, &msg);
if (ret)
return ret;
resp = rpmi_mbox_get_msg_response(&msg);
if (!resp)
return -EINVAL;
if (resp->status)
return rpmi_to_linux_error(le32_to_cpu(resp->status));
rpmi_clk->id = clkid;
rpmi_clk->num_rates = le32_to_cpu(resp->num_rates);
rpmi_clk->transition_latency = le32_to_cpu(resp->transition_latency);
strscpy(rpmi_clk->name, resp->name, RPMI_CLK_NAME_LEN);
format = le32_to_cpu(resp->flags) & RPMI_CLK_TYPE_MASK;
if (format >= RPMI_CLK_TYPE_MAX_IDX)
return -EINVAL;
rpmi_clk->type = format;
return 0;
}
static int rpmi_clk_get_supported_rates(u32 clkid, struct rpmi_clk *rpmi_clk)
{
struct rpmi_clk_context *context = rpmi_clk->context;
struct rpmi_clk_rate_discrete *rate_discrete;
struct rpmi_clk_rate_linear *rate_linear;
struct rpmi_get_supp_rates_tx tx;
struct rpmi_get_supp_rates_rx *resp;
struct rpmi_mbox_message msg;
size_t clk_rate_idx;
int ret, rateidx, j;
tx.clkid = cpu_to_le32(clkid);
tx.clk_rate_idx = 0;
/*
* Make sure we allocate rx buffer sufficient to be accommodate all
* the rates sent in one RPMI message.
*/
struct rpmi_get_supp_rates_rx *rx __free(kfree) =
kzalloc(context->max_msg_data_size, GFP_KERNEL);
if (!rx)
return -ENOMEM;
rpmi_mbox_init_send_with_response(&msg, RPMI_CLK_SRV_GET_SUPPORTED_RATES,
&tx, sizeof(tx), rx, context->max_msg_data_size);
ret = rpmi_mbox_send_message(context->chan, &msg);
if (ret)
return ret;
resp = rpmi_mbox_get_msg_response(&msg);
if (!resp)
return -EINVAL;
if (resp->status)
return rpmi_to_linux_error(le32_to_cpu(resp->status));
if (!le32_to_cpu(resp->returned))
return -EINVAL;
if (rpmi_clk->type == RPMI_CLK_DISCRETE) {
rate_discrete = (struct rpmi_clk_rate_discrete *)resp->rates;
for (rateidx = 0; rateidx < le32_to_cpu(resp->returned); rateidx++) {
rpmi_clk->rates->discrete[rateidx] =
rpmi_clkrate_u64(le32_to_cpu(rate_discrete[rateidx].hi),
le32_to_cpu(rate_discrete[rateidx].lo));
}
/*
* Keep sending the request message until all
* the rates are received.
*/
clk_rate_idx = 0;
while (le32_to_cpu(resp->remaining)) {
clk_rate_idx += le32_to_cpu(resp->returned);
tx.clk_rate_idx = cpu_to_le32(clk_rate_idx);
rpmi_mbox_init_send_with_response(&msg,
RPMI_CLK_SRV_GET_SUPPORTED_RATES,
&tx, sizeof(tx),
rx, context->max_msg_data_size);
ret = rpmi_mbox_send_message(context->chan, &msg);
if (ret)
return ret;
resp = rpmi_mbox_get_msg_response(&msg);
if (!resp)
return -EINVAL;
if (resp->status)
return rpmi_to_linux_error(le32_to_cpu(resp->status));
if (!le32_to_cpu(resp->returned))
return -EINVAL;
for (j = 0; j < le32_to_cpu(resp->returned); j++) {
if (rateidx >= clk_rate_idx + le32_to_cpu(resp->returned))
break;
rpmi_clk->rates->discrete[rateidx++] =
rpmi_clkrate_u64(le32_to_cpu(rate_discrete[j].hi),
le32_to_cpu(rate_discrete[j].lo));
}
}
} else if (rpmi_clk->type == RPMI_CLK_LINEAR) {
rate_linear = (struct rpmi_clk_rate_linear *)resp->rates;
rpmi_clk->rates->linear.min = rpmi_clkrate_u64(le32_to_cpu(rate_linear->min_hi),
le32_to_cpu(rate_linear->min_lo));
rpmi_clk->rates->linear.max = rpmi_clkrate_u64(le32_to_cpu(rate_linear->max_hi),
le32_to_cpu(rate_linear->max_lo));
rpmi_clk->rates->linear.step = rpmi_clkrate_u64(le32_to_cpu(rate_linear->step_hi),
le32_to_cpu(rate_linear->step_lo));
}
return 0;
}
static unsigned long rpmi_clk_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct rpmi_clk *rpmi_clk = to_rpmi_clk(hw);
struct rpmi_clk_context *context = rpmi_clk->context;
struct rpmi_mbox_message msg;
struct rpmi_get_rate_tx tx;
struct rpmi_get_rate_rx rx, *resp;
int ret;
tx.clkid = cpu_to_le32(rpmi_clk->id);
rpmi_mbox_init_send_with_response(&msg, RPMI_CLK_SRV_GET_RATE,
&tx, sizeof(tx), &rx, sizeof(rx));
ret = rpmi_mbox_send_message(context->chan, &msg);
if (ret)
return ret;
resp = rpmi_mbox_get_msg_response(&msg);
if (!resp)
return -EINVAL;
if (resp->status)
return rpmi_to_linux_error(le32_to_cpu(resp->status));
return rpmi_clkrate_u64(le32_to_cpu(resp->hi), le32_to_cpu(resp->lo));
}
static int rpmi_clk_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
struct rpmi_clk *rpmi_clk = to_rpmi_clk(hw);
u64 fmin, fmax, ftmp;
/*
* Keep the requested rate if the clock format
* is of discrete type. Let the platform which
* is actually controlling the clock handle that.
*/
if (rpmi_clk->type == RPMI_CLK_DISCRETE)
return 0;
fmin = rpmi_clk->rates->linear.min;
fmax = rpmi_clk->rates->linear.max;
if (req->rate <= fmin) {
req->rate = fmin;
return 0;
} else if (req->rate >= fmax) {
req->rate = fmax;
return 0;
}
ftmp = req->rate - fmin;
ftmp += rpmi_clk->rates->linear.step - 1;
do_div(ftmp, rpmi_clk->rates->linear.step);
req->rate = ftmp * rpmi_clk->rates->linear.step + fmin;
return 0;
}
static int rpmi_clk_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct rpmi_clk *rpmi_clk = to_rpmi_clk(hw);
struct rpmi_clk_context *context = rpmi_clk->context;
struct rpmi_mbox_message msg;
struct rpmi_set_rate_tx tx;
struct rpmi_set_rate_rx rx, *resp;
int ret;
tx.clkid = cpu_to_le32(rpmi_clk->id);
tx.lo = cpu_to_le32(lower_32_bits(rate));
tx.hi = cpu_to_le32(upper_32_bits(rate));
rpmi_mbox_init_send_with_response(&msg, RPMI_CLK_SRV_SET_RATE,
&tx, sizeof(tx), &rx, sizeof(rx));
ret = rpmi_mbox_send_message(context->chan, &msg);
if (ret)
return ret;
resp = rpmi_mbox_get_msg_response(&msg);
if (!resp)
return -EINVAL;
if (resp->status)
return rpmi_to_linux_error(le32_to_cpu(resp->status));
return 0;
}
static int rpmi_clk_enable(struct clk_hw *hw)
{
struct rpmi_clk *rpmi_clk = to_rpmi_clk(hw);
struct rpmi_clk_context *context = rpmi_clk->context;
struct rpmi_mbox_message msg;
struct rpmi_set_config_tx tx;
struct rpmi_set_config_rx rx, *resp;
int ret;
tx.config = cpu_to_le32(RPMI_CLK_ENABLE);
tx.clkid = cpu_to_le32(rpmi_clk->id);
rpmi_mbox_init_send_with_response(&msg, RPMI_CLK_SRV_SET_CONFIG,
&tx, sizeof(tx), &rx, sizeof(rx));
ret = rpmi_mbox_send_message(context->chan, &msg);
if (ret)
return ret;
resp = rpmi_mbox_get_msg_response(&msg);
if (!resp)
return -EINVAL;
if (resp->status)
return rpmi_to_linux_error(le32_to_cpu(resp->status));
return 0;
}
static void rpmi_clk_disable(struct clk_hw *hw)
{
struct rpmi_clk *rpmi_clk = to_rpmi_clk(hw);
struct rpmi_clk_context *context = rpmi_clk->context;
struct rpmi_mbox_message msg;
struct rpmi_set_config_tx tx;
struct rpmi_set_config_rx rx;
tx.config = cpu_to_le32(RPMI_CLK_DISABLE);
tx.clkid = cpu_to_le32(rpmi_clk->id);
rpmi_mbox_init_send_with_response(&msg, RPMI_CLK_SRV_SET_CONFIG,
&tx, sizeof(tx), &rx, sizeof(rx));
rpmi_mbox_send_message(context->chan, &msg);
}
static const struct clk_ops rpmi_clk_ops = {
.recalc_rate = rpmi_clk_recalc_rate,
.determine_rate = rpmi_clk_determine_rate,
.set_rate = rpmi_clk_set_rate,
.prepare = rpmi_clk_enable,
.unprepare = rpmi_clk_disable,
};
static struct clk_hw *rpmi_clk_enumerate(struct rpmi_clk_context *context, u32 clkid)
{
struct device *dev = context->dev;
unsigned long min_rate, max_rate;
union rpmi_clk_rates *rates;
struct rpmi_clk *rpmi_clk;
struct clk_init_data init = {};
struct clk_hw *clk_hw;
int ret;
rates = devm_kzalloc(dev, sizeof(*rates), GFP_KERNEL);
if (!rates)
return ERR_PTR(-ENOMEM);
rpmi_clk = devm_kzalloc(dev, sizeof(*rpmi_clk), GFP_KERNEL);
if (!rpmi_clk)
return ERR_PTR(-ENOMEM);
rpmi_clk->context = context;
rpmi_clk->rates = rates;
ret = rpmi_clk_get_attrs(clkid, rpmi_clk);
if (ret)
return dev_err_ptr_probe(dev, ret,
"Failed to get clk-%u attributes\n",
clkid);
ret = rpmi_clk_get_supported_rates(clkid, rpmi_clk);
if (ret)
return dev_err_ptr_probe(dev, ret,
"Get supported rates failed for clk-%u\n",
clkid);
init.flags = CLK_GET_RATE_NOCACHE;
init.num_parents = 0;
init.ops = &rpmi_clk_ops;
init.name = rpmi_clk->name;
clk_hw = &rpmi_clk->hw;
clk_hw->init = &init;
ret = devm_clk_hw_register(dev, clk_hw);
if (ret)
return dev_err_ptr_probe(dev, ret,
"Unable to register clk-%u\n",
clkid);
if (rpmi_clk->type == RPMI_CLK_DISCRETE) {
min_rate = rpmi_clk->rates->discrete[0];
max_rate = rpmi_clk->rates->discrete[rpmi_clk->num_rates - 1];
} else {
min_rate = rpmi_clk->rates->linear.min;
max_rate = rpmi_clk->rates->linear.max;
}
clk_hw_set_rate_range(clk_hw, min_rate, max_rate);
return clk_hw;
}
static void rpmi_clk_mbox_chan_release(void *data)
{
struct mbox_chan *chan = data;
mbox_free_channel(chan);
}
static int rpmi_clk_probe(struct platform_device *pdev)
{
int ret;
unsigned int num_clocks, i;
struct clk_hw_onecell_data *clk_data;
struct rpmi_clk_context *context;
struct rpmi_mbox_message msg;
struct clk_hw *hw_ptr;
struct device *dev = &pdev->dev;
context = devm_kzalloc(dev, sizeof(*context), GFP_KERNEL);
if (!context)
return -ENOMEM;
context->dev = dev;
platform_set_drvdata(pdev, context);
context->client.dev = context->dev;
context->client.rx_callback = NULL;
context->client.tx_block = false;
context->client.knows_txdone = true;
context->client.tx_tout = 0;
context->chan = mbox_request_channel(&context->client, 0);
if (IS_ERR(context->chan))
return PTR_ERR(context->chan);
ret = devm_add_action_or_reset(dev, rpmi_clk_mbox_chan_release, context->chan);
if (ret)
return dev_err_probe(dev, ret, "Failed to add rpmi mbox channel cleanup\n");
rpmi_mbox_init_get_attribute(&msg, RPMI_MBOX_ATTR_SPEC_VERSION);
ret = rpmi_mbox_send_message(context->chan, &msg);
if (ret)
return dev_err_probe(dev, ret, "Failed to get spec version\n");
if (msg.attr.value < RPMI_MKVER(1, 0)) {
return dev_err_probe(dev, -EINVAL,
"msg protocol version mismatch, expected 0x%x, found 0x%x\n",
RPMI_MKVER(1, 0), msg.attr.value);
}
rpmi_mbox_init_get_attribute(&msg, RPMI_MBOX_ATTR_SERVICEGROUP_ID);
ret = rpmi_mbox_send_message(context->chan, &msg);
if (ret)
return dev_err_probe(dev, ret, "Failed to get service group ID\n");
if (msg.attr.value != RPMI_SRVGRP_CLOCK) {
return dev_err_probe(dev, -EINVAL,
"service group match failed, expected 0x%x, found 0x%x\n",
RPMI_SRVGRP_CLOCK, msg.attr.value);
}
rpmi_mbox_init_get_attribute(&msg, RPMI_MBOX_ATTR_SERVICEGROUP_VERSION);
ret = rpmi_mbox_send_message(context->chan, &msg);
if (ret)
return dev_err_probe(dev, ret, "Failed to get service group version\n");
if (msg.attr.value < RPMI_MKVER(1, 0)) {
return dev_err_probe(dev, -EINVAL,
"service group version failed, expected 0x%x, found 0x%x\n",
RPMI_MKVER(1, 0), msg.attr.value);
}
rpmi_mbox_init_get_attribute(&msg, RPMI_MBOX_ATTR_MAX_MSG_DATA_SIZE);
ret = rpmi_mbox_send_message(context->chan, &msg);
if (ret)
return dev_err_probe(dev, ret, "Failed to get max message data size\n");
context->max_msg_data_size = msg.attr.value;
num_clocks = rpmi_clk_get_num_clocks(context);
if (!num_clocks)
return dev_err_probe(dev, -ENODEV, "No clocks found\n");
clk_data = devm_kzalloc(dev, struct_size(clk_data, hws, num_clocks),
GFP_KERNEL);
if (!clk_data)
return dev_err_probe(dev, -ENOMEM, "No memory for clock data\n");
clk_data->num = num_clocks;
for (i = 0; i < clk_data->num; i++) {
hw_ptr = rpmi_clk_enumerate(context, i);
if (IS_ERR(hw_ptr)) {
return dev_err_probe(dev, PTR_ERR(hw_ptr),
"Failed to register clk-%d\n", i);
}
clk_data->hws[i] = hw_ptr;
}
ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, clk_data);
if (ret)
return dev_err_probe(dev, ret, "Failed to register clock HW provider\n");
return 0;
}
static const struct of_device_id rpmi_clk_of_match[] = {
{ .compatible = "riscv,rpmi-clock" },
{ }
};
MODULE_DEVICE_TABLE(of, rpmi_clk_of_match);
static struct platform_driver rpmi_clk_driver = {
.driver = {
.name = "riscv-rpmi-clock",
.of_match_table = rpmi_clk_of_match,
},
.probe = rpmi_clk_probe,
};
module_platform_driver(rpmi_clk_driver);
MODULE_AUTHOR("Rahul Pathak <rpathak@ventanamicro.com>");
MODULE_DESCRIPTION("Clock Driver based on RPMI message protocol");
MODULE_LICENSE("GPL");

View File

@ -634,6 +634,13 @@ config RISCV_IMSIC
select GENERIC_MSI_IRQ select GENERIC_MSI_IRQ
select IRQ_MSI_LIB select IRQ_MSI_LIB
config RISCV_RPMI_SYSMSI
bool
depends on RISCV && MAILBOX
select IRQ_DOMAIN_HIERARCHY
select GENERIC_MSI_IRQ
default RISCV
config SIFIVE_PLIC config SIFIVE_PLIC
bool bool
depends on RISCV depends on RISCV

View File

@ -106,6 +106,7 @@ obj-$(CONFIG_RISCV_INTC) += irq-riscv-intc.o
obj-$(CONFIG_RISCV_APLIC) += irq-riscv-aplic-main.o irq-riscv-aplic-direct.o obj-$(CONFIG_RISCV_APLIC) += irq-riscv-aplic-main.o irq-riscv-aplic-direct.o
obj-$(CONFIG_RISCV_APLIC_MSI) += irq-riscv-aplic-msi.o obj-$(CONFIG_RISCV_APLIC_MSI) += irq-riscv-aplic-msi.o
obj-$(CONFIG_RISCV_IMSIC) += irq-riscv-imsic-state.o irq-riscv-imsic-early.o irq-riscv-imsic-platform.o obj-$(CONFIG_RISCV_IMSIC) += irq-riscv-imsic-state.o irq-riscv-imsic-early.o irq-riscv-imsic-platform.o
obj-$(CONFIG_RISCV_RPMI_SYSMSI) += irq-riscv-rpmi-sysmsi.o
obj-$(CONFIG_SIFIVE_PLIC) += irq-sifive-plic.o obj-$(CONFIG_SIFIVE_PLIC) += irq-sifive-plic.o
obj-$(CONFIG_STARFIVE_JH8100_INTC) += irq-starfive-jh8100-intc.o obj-$(CONFIG_STARFIVE_JH8100_INTC) += irq-starfive-jh8100-intc.o
obj-$(CONFIG_ACLINT_SSWI) += irq-aclint-sswi.o obj-$(CONFIG_ACLINT_SSWI) += irq-aclint-sswi.o

View File

@ -7,6 +7,7 @@
#define pr_fmt(fmt) "riscv-imsic: " fmt #define pr_fmt(fmt) "riscv-imsic: " fmt
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/cpu.h> #include <linux/cpu.h>
#include <linux/export.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/io.h> #include <linux/io.h>
@ -233,6 +234,7 @@ struct fwnode_handle *imsic_acpi_get_fwnode(struct device *dev)
{ {
return imsic_acpi_fwnode; return imsic_acpi_fwnode;
} }
EXPORT_SYMBOL_GPL(imsic_acpi_get_fwnode);
static int __init imsic_early_acpi_init(union acpi_subtable_headers *header, static int __init imsic_early_acpi_init(union acpi_subtable_headers *header,
const unsigned long end) const unsigned long end)

View File

@ -0,0 +1,328 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (C) 2025 Ventana Micro Systems Inc. */
#include <linux/acpi.h>
#include <linux/bits.h>
#include <linux/bug.h>
#include <linux/device.h>
#include <linux/device/devres.h>
#include <linux/dev_printk.h>
#include <linux/errno.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/irqchip/riscv-imsic.h>
#include <linux/mailbox_client.h>
#include <linux/mailbox/riscv-rpmi-message.h>
#include <linux/module.h>
#include <linux/msi.h>
#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/types.h>
struct rpmi_sysmsi_get_attrs_rx {
__le32 status;
__le32 sys_num_msi;
__le32 flag0;
__le32 flag1;
};
#define RPMI_SYSMSI_MSI_ATTRIBUTES_FLAG0_PREF_PRIV BIT(0)
struct rpmi_sysmsi_set_msi_state_tx {
__le32 sys_msi_index;
__le32 sys_msi_state;
};
struct rpmi_sysmsi_set_msi_state_rx {
__le32 status;
};
#define RPMI_SYSMSI_MSI_STATE_ENABLE BIT(0)
#define RPMI_SYSMSI_MSI_STATE_PENDING BIT(1)
struct rpmi_sysmsi_set_msi_target_tx {
__le32 sys_msi_index;
__le32 sys_msi_address_low;
__le32 sys_msi_address_high;
__le32 sys_msi_data;
};
struct rpmi_sysmsi_set_msi_target_rx {
__le32 status;
};
struct rpmi_sysmsi_priv {
struct device *dev;
struct mbox_client client;
struct mbox_chan *chan;
u32 nr_irqs;
u32 gsi_base;
};
static int rpmi_sysmsi_get_num_msi(struct rpmi_sysmsi_priv *priv)
{
struct rpmi_sysmsi_get_attrs_rx rx;
struct rpmi_mbox_message msg;
int ret;
rpmi_mbox_init_send_with_response(&msg, RPMI_SYSMSI_SRV_GET_ATTRIBUTES,
NULL, 0, &rx, sizeof(rx));
ret = rpmi_mbox_send_message(priv->chan, &msg);
if (ret)
return ret;
if (rx.status)
return rpmi_to_linux_error(le32_to_cpu(rx.status));
return le32_to_cpu(rx.sys_num_msi);
}
static int rpmi_sysmsi_set_msi_state(struct rpmi_sysmsi_priv *priv,
u32 sys_msi_index, u32 sys_msi_state)
{
struct rpmi_sysmsi_set_msi_state_tx tx;
struct rpmi_sysmsi_set_msi_state_rx rx;
struct rpmi_mbox_message msg;
int ret;
tx.sys_msi_index = cpu_to_le32(sys_msi_index);
tx.sys_msi_state = cpu_to_le32(sys_msi_state);
rpmi_mbox_init_send_with_response(&msg, RPMI_SYSMSI_SRV_SET_MSI_STATE,
&tx, sizeof(tx), &rx, sizeof(rx));
ret = rpmi_mbox_send_message(priv->chan, &msg);
if (ret)
return ret;
if (rx.status)
return rpmi_to_linux_error(le32_to_cpu(rx.status));
return 0;
}
static int rpmi_sysmsi_set_msi_target(struct rpmi_sysmsi_priv *priv,
u32 sys_msi_index, struct msi_msg *m)
{
struct rpmi_sysmsi_set_msi_target_tx tx;
struct rpmi_sysmsi_set_msi_target_rx rx;
struct rpmi_mbox_message msg;
int ret;
tx.sys_msi_index = cpu_to_le32(sys_msi_index);
tx.sys_msi_address_low = cpu_to_le32(m->address_lo);
tx.sys_msi_address_high = cpu_to_le32(m->address_hi);
tx.sys_msi_data = cpu_to_le32(m->data);
rpmi_mbox_init_send_with_response(&msg, RPMI_SYSMSI_SRV_SET_MSI_TARGET,
&tx, sizeof(tx), &rx, sizeof(rx));
ret = rpmi_mbox_send_message(priv->chan, &msg);
if (ret)
return ret;
if (rx.status)
return rpmi_to_linux_error(le32_to_cpu(rx.status));
return 0;
}
static void rpmi_sysmsi_irq_mask(struct irq_data *d)
{
struct rpmi_sysmsi_priv *priv = irq_data_get_irq_chip_data(d);
irq_hw_number_t hwirq = irqd_to_hwirq(d);
int ret;
ret = rpmi_sysmsi_set_msi_state(priv, hwirq, 0);
if (ret)
dev_warn(priv->dev, "Failed to mask hwirq %lu (error %d)\n", hwirq, ret);
irq_chip_mask_parent(d);
}
static void rpmi_sysmsi_irq_unmask(struct irq_data *d)
{
struct rpmi_sysmsi_priv *priv = irq_data_get_irq_chip_data(d);
irq_hw_number_t hwirq = irqd_to_hwirq(d);
int ret;
irq_chip_unmask_parent(d);
ret = rpmi_sysmsi_set_msi_state(priv, hwirq, RPMI_SYSMSI_MSI_STATE_ENABLE);
if (ret)
dev_warn(priv->dev, "Failed to unmask hwirq %lu (error %d)\n", hwirq, ret);
}
static void rpmi_sysmsi_write_msg(struct irq_data *d, struct msi_msg *msg)
{
struct rpmi_sysmsi_priv *priv = irq_data_get_irq_chip_data(d);
irq_hw_number_t hwirq = irqd_to_hwirq(d);
int ret;
/* For zeroed MSI, do nothing as of now */
if (!msg->address_hi && !msg->address_lo && !msg->data)
return;
ret = rpmi_sysmsi_set_msi_target(priv, hwirq, msg);
if (ret)
dev_warn(priv->dev, "Failed to set target for hwirq %lu (error %d)\n", hwirq, ret);
}
static void rpmi_sysmsi_set_desc(msi_alloc_info_t *arg, struct msi_desc *desc)
{
arg->desc = desc;
arg->hwirq = desc->data.icookie.value;
}
static int rpmi_sysmsi_translate(struct irq_domain *d, struct irq_fwspec *fwspec,
unsigned long *hwirq, unsigned int *type)
{
struct msi_domain_info *info = d->host_data;
struct rpmi_sysmsi_priv *priv = info->data;
if (WARN_ON(fwspec->param_count < 1))
return -EINVAL;
/* For DT, gsi_base is always zero. */
*hwirq = fwspec->param[0] - priv->gsi_base;
*type = IRQ_TYPE_NONE;
return 0;
}
static const struct msi_domain_template rpmi_sysmsi_template = {
.chip = {
.name = "RPMI-SYSMSI",
.irq_mask = rpmi_sysmsi_irq_mask,
.irq_unmask = rpmi_sysmsi_irq_unmask,
#ifdef CONFIG_SMP
.irq_set_affinity = irq_chip_set_affinity_parent,
#endif
.irq_write_msi_msg = rpmi_sysmsi_write_msg,
.flags = IRQCHIP_SET_TYPE_MASKED |
IRQCHIP_SKIP_SET_WAKE |
IRQCHIP_MASK_ON_SUSPEND,
},
.ops = {
.set_desc = rpmi_sysmsi_set_desc,
.msi_translate = rpmi_sysmsi_translate,
},
.info = {
.bus_token = DOMAIN_BUS_WIRED_TO_MSI,
.flags = MSI_FLAG_USE_DEV_FWNODE,
.handler = handle_simple_irq,
.handler_name = "simple",
},
};
static int rpmi_sysmsi_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct rpmi_sysmsi_priv *priv;
struct fwnode_handle *fwnode;
u32 id;
int rc;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->dev = dev;
/* Setup mailbox client */
priv->client.dev = priv->dev;
priv->client.rx_callback = NULL;
priv->client.tx_block = false;
priv->client.knows_txdone = true;
priv->client.tx_tout = 0;
/* Request mailbox channel */
priv->chan = mbox_request_channel(&priv->client, 0);
if (IS_ERR(priv->chan))
return PTR_ERR(priv->chan);
/* Get number of system MSIs */
rc = rpmi_sysmsi_get_num_msi(priv);
if (rc < 1) {
mbox_free_channel(priv->chan);
if (rc)
return dev_err_probe(dev, rc, "Failed to get number of system MSIs\n");
else
return dev_err_probe(dev, -ENODEV, "No system MSIs found\n");
}
priv->nr_irqs = rc;
fwnode = dev_fwnode(dev);
if (is_acpi_node(fwnode)) {
u32 nr_irqs;
rc = riscv_acpi_get_gsi_info(fwnode, &priv->gsi_base, &id,
&nr_irqs, NULL);
if (rc) {
dev_err(dev, "failed to find GSI mapping\n");
return rc;
}
/* Update with actual GSI range */
if (nr_irqs != priv->nr_irqs)
riscv_acpi_update_gsi_range(priv->gsi_base, priv->nr_irqs);
}
/*
* The device MSI domain for platform devices on RISC-V architecture
* is only available after the MSI controller driver is probed so,
* explicitly configure here.
*/
if (!dev_get_msi_domain(dev)) {
/*
* The device MSI domain for OF devices is only set at the
* time of populating/creating OF device. If the device MSI
* domain is discovered later after the OF device is created
* then we need to set it explicitly before using any platform
* MSI functions.
*/
if (is_of_node(fwnode)) {
of_msi_configure(dev, dev_of_node(dev));
} else if (is_acpi_device_node(fwnode)) {
struct irq_domain *msi_domain;
msi_domain = irq_find_matching_fwnode(imsic_acpi_get_fwnode(dev),
DOMAIN_BUS_PLATFORM_MSI);
dev_set_msi_domain(dev, msi_domain);
}
if (!dev_get_msi_domain(dev)) {
mbox_free_channel(priv->chan);
return -EPROBE_DEFER;
}
}
if (!msi_create_device_irq_domain(dev, MSI_DEFAULT_DOMAIN,
&rpmi_sysmsi_template,
priv->nr_irqs, priv, priv)) {
mbox_free_channel(priv->chan);
return dev_err_probe(dev, -ENOMEM, "failed to create MSI irq domain\n");
}
#ifdef CONFIG_ACPI
struct acpi_device *adev = ACPI_COMPANION(dev);
if (adev)
acpi_dev_clear_dependencies(adev);
#endif
dev_info(dev, "%u system MSIs registered\n", priv->nr_irqs);
return 0;
}
static const struct of_device_id rpmi_sysmsi_match[] = {
{ .compatible = "riscv,rpmi-system-msi" },
{}
};
static const struct acpi_device_id acpi_rpmi_sysmsi_match[] = {
{ "RSCV0006" },
{}
};
MODULE_DEVICE_TABLE(acpi, acpi_rpmi_sysmsi_match);
static struct platform_driver rpmi_sysmsi_driver = {
.driver = {
.name = "rpmi-sysmsi",
.of_match_table = rpmi_sysmsi_match,
.acpi_match_table = acpi_rpmi_sysmsi_match,
},
.probe = rpmi_sysmsi_probe,
};
builtin_platform_driver(rpmi_sysmsi_driver);

View File

@ -369,4 +369,15 @@ config BCM74110_MAILBOX
processor and coprocessor that handles various power management task processor and coprocessor that handles various power management task
and more. and more.
config RISCV_SBI_MPXY_MBOX
tristate "RISC-V SBI Message Proxy (MPXY) Mailbox"
depends on RISCV_SBI
default RISCV
help
Mailbox driver implementation for RISC-V SBI Message Proxy (MPXY)
extension. This mailbox driver is used to send messages to the
remote processor through the SBI implementation (M-mode firmware
or HS-mode hypervisor). Say Y here if you want to have this support.
If unsure say N.
endif endif

View File

@ -78,3 +78,5 @@ obj-$(CONFIG_THEAD_TH1520_MBOX) += mailbox-th1520.o
obj-$(CONFIG_CIX_MBOX) += cix-mailbox.o obj-$(CONFIG_CIX_MBOX) += cix-mailbox.o
obj-$(CONFIG_BCM74110_MAILBOX) += bcm74110-mailbox.o obj-$(CONFIG_BCM74110_MAILBOX) += bcm74110-mailbox.o
obj-$(CONFIG_RISCV_SBI_MPXY_MBOX) += riscv-sbi-mpxy-mbox.o

View File

@ -15,6 +15,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/property.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include "mailbox.h" #include "mailbox.h"
@ -383,34 +384,56 @@ EXPORT_SYMBOL_GPL(mbox_bind_client);
*/ */
struct mbox_chan *mbox_request_channel(struct mbox_client *cl, int index) struct mbox_chan *mbox_request_channel(struct mbox_client *cl, int index)
{ {
struct device *dev = cl->dev; struct fwnode_reference_args fwspec;
struct fwnode_handle *fwnode;
struct mbox_controller *mbox; struct mbox_controller *mbox;
struct of_phandle_args spec; struct of_phandle_args spec;
struct mbox_chan *chan; struct mbox_chan *chan;
struct device *dev;
unsigned int i;
int ret; int ret;
if (!dev || !dev->of_node) { dev = cl->dev;
pr_debug("%s: No owner device node\n", __func__); if (!dev) {
pr_debug("No owner device\n");
return ERR_PTR(-ENODEV); return ERR_PTR(-ENODEV);
} }
ret = of_parse_phandle_with_args(dev->of_node, "mboxes", "#mbox-cells", fwnode = dev_fwnode(dev);
index, &spec); if (!fwnode) {
dev_dbg(dev, "No owner fwnode\n");
return ERR_PTR(-ENODEV);
}
ret = fwnode_property_get_reference_args(fwnode, "mboxes", "#mbox-cells",
0, index, &fwspec);
if (ret) { if (ret) {
dev_err(dev, "%s: can't parse \"mboxes\" property\n", __func__); dev_err(dev, "%s: can't parse \"%s\" property\n", __func__, "mboxes");
return ERR_PTR(ret); return ERR_PTR(ret);
} }
spec.np = to_of_node(fwspec.fwnode);
spec.args_count = fwspec.nargs;
for (i = 0; i < spec.args_count; i++)
spec.args[i] = fwspec.args[i];
scoped_guard(mutex, &con_mutex) { scoped_guard(mutex, &con_mutex) {
chan = ERR_PTR(-EPROBE_DEFER); chan = ERR_PTR(-EPROBE_DEFER);
list_for_each_entry(mbox, &mbox_cons, node) list_for_each_entry(mbox, &mbox_cons, node) {
if (mbox->dev->of_node == spec.np) { if (device_match_fwnode(mbox->dev, fwspec.fwnode)) {
chan = mbox->of_xlate(mbox, &spec); if (mbox->fw_xlate) {
if (!IS_ERR(chan)) chan = mbox->fw_xlate(mbox, &fwspec);
break; if (!IS_ERR(chan))
break;
} else if (mbox->of_xlate) {
chan = mbox->of_xlate(mbox, &spec);
if (!IS_ERR(chan))
break;
}
} }
}
of_node_put(spec.np); fwnode_handle_put(fwspec.fwnode);
if (IS_ERR(chan)) if (IS_ERR(chan))
return chan; return chan;
@ -427,15 +450,8 @@ EXPORT_SYMBOL_GPL(mbox_request_channel);
struct mbox_chan *mbox_request_channel_byname(struct mbox_client *cl, struct mbox_chan *mbox_request_channel_byname(struct mbox_client *cl,
const char *name) const char *name)
{ {
struct device_node *np = cl->dev->of_node; int index = device_property_match_string(cl->dev, "mbox-names", name);
int index;
if (!np) {
dev_err(cl->dev, "%s() currently only supports DT\n", __func__);
return ERR_PTR(-EINVAL);
}
index = of_property_match_string(np, "mbox-names", name);
if (index < 0) { if (index < 0) {
dev_err(cl->dev, "%s() could not locate channel named \"%s\"\n", dev_err(cl->dev, "%s() could not locate channel named \"%s\"\n",
__func__, name); __func__, name);
@ -470,9 +486,8 @@ void mbox_free_channel(struct mbox_chan *chan)
} }
EXPORT_SYMBOL_GPL(mbox_free_channel); EXPORT_SYMBOL_GPL(mbox_free_channel);
static struct mbox_chan * static struct mbox_chan *fw_mbox_index_xlate(struct mbox_controller *mbox,
of_mbox_index_xlate(struct mbox_controller *mbox, const struct fwnode_reference_args *sp)
const struct of_phandle_args *sp)
{ {
int ind = sp->args[0]; int ind = sp->args[0];
@ -523,8 +538,8 @@ int mbox_controller_register(struct mbox_controller *mbox)
spin_lock_init(&chan->lock); spin_lock_init(&chan->lock);
} }
if (!mbox->of_xlate) if (!mbox->fw_xlate && !mbox->of_xlate)
mbox->of_xlate = of_mbox_index_xlate; mbox->fw_xlate = fw_mbox_index_xlate;
scoped_guard(mutex, &con_mutex) scoped_guard(mutex, &con_mutex)
list_add_tail(&mbox->node, &mbox_cons); list_add_tail(&mbox->node, &mbox_cons);

File diff suppressed because it is too large Load Diff

View File

@ -173,6 +173,22 @@ static inline void cpu_to_le32_array(u32 *buf, unsigned int words)
} }
} }
static inline void memcpy_from_le32(u32 *dst, const __le32 *src, size_t words)
{
size_t i;
for (i = 0; i < words; i++)
dst[i] = le32_to_cpu(src[i]);
}
static inline void memcpy_to_le32(__le32 *dst, const u32 *src, size_t words)
{
size_t i;
for (i = 0; i < words; i++)
dst[i] = cpu_to_le32(src[i]);
}
static inline void be16_add_cpu(__be16 *var, u16 val) static inline void be16_add_cpu(__be16 *var, u16 val)
{ {
*var = cpu_to_be16(be16_to_cpu(*var) + val); *var = cpu_to_be16(be16_to_cpu(*var) + val);

View File

@ -0,0 +1,243 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (C) 2025 Ventana Micro Systems Inc. */
#ifndef _LINUX_RISCV_RPMI_MESSAGE_H_
#define _LINUX_RISCV_RPMI_MESSAGE_H_
#include <linux/errno.h>
#include <linux/mailbox_client.h>
#include <linux/types.h>
#include <linux/wordpart.h>
/* RPMI version encode/decode macros */
#define RPMI_VER_MAJOR(__ver) upper_16_bits(__ver)
#define RPMI_VER_MINOR(__ver) lower_16_bits(__ver)
#define RPMI_MKVER(__maj, __min) (((u32)(__maj) << 16) | (u16)(__min))
/* RPMI message header */
struct rpmi_message_header {
__le16 servicegroup_id;
u8 service_id;
u8 flags;
__le16 datalen;
__le16 token;
};
/* RPMI message */
struct rpmi_message {
struct rpmi_message_header header;
u8 data[];
};
/* RPMI notification event */
struct rpmi_notification_event {
__le16 event_datalen;
u8 event_id;
u8 reserved;
u8 event_data[];
};
/* RPMI error codes */
enum rpmi_error_codes {
RPMI_SUCCESS = 0,
RPMI_ERR_FAILED = -1,
RPMI_ERR_NOTSUPP = -2,
RPMI_ERR_INVALID_PARAM = -3,
RPMI_ERR_DENIED = -4,
RPMI_ERR_INVALID_ADDR = -5,
RPMI_ERR_ALREADY = -6,
RPMI_ERR_EXTENSION = -7,
RPMI_ERR_HW_FAULT = -8,
RPMI_ERR_BUSY = -9,
RPMI_ERR_INVALID_STATE = -10,
RPMI_ERR_BAD_RANGE = -11,
RPMI_ERR_TIMEOUT = -12,
RPMI_ERR_IO = -13,
RPMI_ERR_NO_DATA = -14,
RPMI_ERR_RESERVED_START = -15,
RPMI_ERR_RESERVED_END = -127,
RPMI_ERR_VENDOR_START = -128,
};
static inline int rpmi_to_linux_error(int rpmi_error)
{
switch (rpmi_error) {
case RPMI_SUCCESS:
return 0;
case RPMI_ERR_INVALID_PARAM:
case RPMI_ERR_BAD_RANGE:
case RPMI_ERR_INVALID_STATE:
return -EINVAL;
case RPMI_ERR_DENIED:
return -EPERM;
case RPMI_ERR_INVALID_ADDR:
case RPMI_ERR_HW_FAULT:
return -EFAULT;
case RPMI_ERR_ALREADY:
return -EALREADY;
case RPMI_ERR_BUSY:
return -EBUSY;
case RPMI_ERR_TIMEOUT:
return -ETIMEDOUT;
case RPMI_ERR_IO:
return -ECOMM;
case RPMI_ERR_FAILED:
case RPMI_ERR_NOTSUPP:
case RPMI_ERR_NO_DATA:
case RPMI_ERR_EXTENSION:
default:
return -EOPNOTSUPP;
}
}
/* RPMI service group IDs */
#define RPMI_SRVGRP_SYSTEM_MSI 0x00002
#define RPMI_SRVGRP_CLOCK 0x00008
/* RPMI clock service IDs */
enum rpmi_clock_service_id {
RPMI_CLK_SRV_ENABLE_NOTIFICATION = 0x01,
RPMI_CLK_SRV_GET_NUM_CLOCKS = 0x02,
RPMI_CLK_SRV_GET_ATTRIBUTES = 0x03,
RPMI_CLK_SRV_GET_SUPPORTED_RATES = 0x04,
RPMI_CLK_SRV_SET_CONFIG = 0x05,
RPMI_CLK_SRV_GET_CONFIG = 0x06,
RPMI_CLK_SRV_SET_RATE = 0x07,
RPMI_CLK_SRV_GET_RATE = 0x08,
RPMI_CLK_SRV_ID_MAX_COUNT
};
/* RPMI system MSI service IDs */
enum rpmi_sysmsi_service_id {
RPMI_SYSMSI_SRV_ENABLE_NOTIFICATION = 0x01,
RPMI_SYSMSI_SRV_GET_ATTRIBUTES = 0x02,
RPMI_SYSMSI_SRV_GET_MSI_ATTRIBUTES = 0x03,
RPMI_SYSMSI_SRV_SET_MSI_STATE = 0x04,
RPMI_SYSMSI_SRV_GET_MSI_STATE = 0x05,
RPMI_SYSMSI_SRV_SET_MSI_TARGET = 0x06,
RPMI_SYSMSI_SRV_GET_MSI_TARGET = 0x07,
RPMI_SYSMSI_SRV_ID_MAX_COUNT
};
/* RPMI Linux mailbox attribute IDs */
enum rpmi_mbox_attribute_id {
RPMI_MBOX_ATTR_SPEC_VERSION,
RPMI_MBOX_ATTR_MAX_MSG_DATA_SIZE,
RPMI_MBOX_ATTR_SERVICEGROUP_ID,
RPMI_MBOX_ATTR_SERVICEGROUP_VERSION,
RPMI_MBOX_ATTR_IMPL_ID,
RPMI_MBOX_ATTR_IMPL_VERSION,
RPMI_MBOX_ATTR_MAX_ID
};
/* RPMI Linux mailbox message types */
enum rpmi_mbox_message_type {
RPMI_MBOX_MSG_TYPE_GET_ATTRIBUTE,
RPMI_MBOX_MSG_TYPE_SET_ATTRIBUTE,
RPMI_MBOX_MSG_TYPE_SEND_WITH_RESPONSE,
RPMI_MBOX_MSG_TYPE_SEND_WITHOUT_RESPONSE,
RPMI_MBOX_MSG_TYPE_NOTIFICATION_EVENT,
RPMI_MBOX_MSG_MAX_TYPE
};
/* RPMI Linux mailbox message instance */
struct rpmi_mbox_message {
enum rpmi_mbox_message_type type;
union {
struct {
enum rpmi_mbox_attribute_id id;
u32 value;
} attr;
struct {
u32 service_id;
void *request;
unsigned long request_len;
void *response;
unsigned long max_response_len;
unsigned long out_response_len;
} data;
struct {
u16 event_datalen;
u8 event_id;
u8 *event_data;
} notif;
};
int error;
};
/* RPMI Linux mailbox message helper routines */
static inline void rpmi_mbox_init_get_attribute(struct rpmi_mbox_message *msg,
enum rpmi_mbox_attribute_id id)
{
msg->type = RPMI_MBOX_MSG_TYPE_GET_ATTRIBUTE;
msg->attr.id = id;
msg->attr.value = 0;
msg->error = 0;
}
static inline void rpmi_mbox_init_set_attribute(struct rpmi_mbox_message *msg,
enum rpmi_mbox_attribute_id id,
u32 value)
{
msg->type = RPMI_MBOX_MSG_TYPE_SET_ATTRIBUTE;
msg->attr.id = id;
msg->attr.value = value;
msg->error = 0;
}
static inline void rpmi_mbox_init_send_with_response(struct rpmi_mbox_message *msg,
u32 service_id,
void *request,
unsigned long request_len,
void *response,
unsigned long max_response_len)
{
msg->type = RPMI_MBOX_MSG_TYPE_SEND_WITH_RESPONSE;
msg->data.service_id = service_id;
msg->data.request = request;
msg->data.request_len = request_len;
msg->data.response = response;
msg->data.max_response_len = max_response_len;
msg->data.out_response_len = 0;
msg->error = 0;
}
static inline void rpmi_mbox_init_send_without_response(struct rpmi_mbox_message *msg,
u32 service_id,
void *request,
unsigned long request_len)
{
msg->type = RPMI_MBOX_MSG_TYPE_SEND_WITHOUT_RESPONSE;
msg->data.service_id = service_id;
msg->data.request = request;
msg->data.request_len = request_len;
msg->data.response = NULL;
msg->data.max_response_len = 0;
msg->data.out_response_len = 0;
msg->error = 0;
}
static inline void *rpmi_mbox_get_msg_response(struct rpmi_mbox_message *msg)
{
return msg ? msg->data.response : NULL;
}
static inline int rpmi_mbox_send_message(struct mbox_chan *chan,
struct rpmi_mbox_message *msg)
{
int ret;
/* Send message for the underlying mailbox channel */
ret = mbox_send_message(chan, msg);
if (ret < 0)
return ret;
/* Explicitly signal txdone for mailbox channel */
ret = msg->error;
mbox_client_txdone(chan, ret);
return ret;
}
#endif /* _LINUX_RISCV_RPMI_MESSAGE_H_ */

View File

@ -66,6 +66,7 @@ struct mbox_chan_ops {
* no interrupt rises. Ignored if 'txdone_irq' is set. * no interrupt rises. Ignored if 'txdone_irq' is set.
* @txpoll_period: If 'txdone_poll' is in effect, the API polls for * @txpoll_period: If 'txdone_poll' is in effect, the API polls for
* last TX's status after these many millisecs * last TX's status after these many millisecs
* @fw_xlate: Controller driver specific mapping of channel via fwnode
* @of_xlate: Controller driver specific mapping of channel via DT * @of_xlate: Controller driver specific mapping of channel via DT
* @poll_hrt: API private. hrtimer used to poll for TXDONE on all * @poll_hrt: API private. hrtimer used to poll for TXDONE on all
* channels. * channels.
@ -79,6 +80,8 @@ struct mbox_controller {
bool txdone_irq; bool txdone_irq;
bool txdone_poll; bool txdone_poll;
unsigned txpoll_period; unsigned txpoll_period;
struct mbox_chan *(*fw_xlate)(struct mbox_controller *mbox,
const struct fwnode_reference_args *sp);
struct mbox_chan *(*of_xlate)(struct mbox_controller *mbox, struct mbox_chan *(*of_xlate)(struct mbox_controller *mbox,
const struct of_phandle_args *sp); const struct of_phandle_args *sp);
/* Internal to API */ /* Internal to API */