All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v5 0/2] Add initial ARM MHUv3 mailbox support
@ 2024-04-18 10:52 ` Cristian Marussi
  0 siblings, 0 replies; 6+ messages in thread
From: Cristian Marussi @ 2024-04-18 10:52 UTC (permalink / raw)
  To: linux-kernel, linux-arm-kernel, devicetree
  Cc: sudeep.holla, cristian.marussi, jassisinghbrar, robh+dt,
	krzysztof.kozlowski+dt, conor+dt

Hi,

This series adds support for the new ARM Message Handling Unit v3 mailbox
controller [1].

The ARM MHUv3 can optionally support various extensions, enabling the
usage of different transport protocols.

Patch [2/2] adds a platform driver which, as of now, provides support only
for the Doorbell extension using the combined interrupt.

On the other side, bindings in [1/2] are introduced for all the extensions
described by the specification, as long as they are of interest to an
entity running from Normal world, like Linux: as such, Doorbell, FIFO and
FastChannel extensions are documented.

In these regards, note that the ARM MHUv3 controller can optionally
implement a considerable number of interrupts to express a great deal of
events and many of such interrupts are defined as being per-channel: with
the total maximum amount of possibly implemented channels across all
extensions being 1216 (1024+128+64), it would mean *a lot* of
interrupt-names to enumerate in the bindings.

For the sake of simplicity the binding as of now only introduces interrupt
names for a mere 8-channels in the range (0,7) for each per-channel
interrupt type: the idea is to leave open the possibility to add more to
this list of numbered items only when (and if) new real HW appears that
effectively needs more than 8 channels. (like AMBA, where the maximum
number of IRQ was progressively increased when needed, AFAIU).

Based on v6.9-rc1, tested on ARM TCS23 [2]
(TCS23 reference SW stack is still to be made fully publicly available)

Thanks,
Cristian

[1]: https://developer.arm.com/documentation/aes0072/aa/?lang=en
[2]: https://community.arm.com/arm-community-blogs/b/tools-software-ides-blog/posts/total-compute-solutions-platform-software-stack-and-fvp

---
v4 -> v5
- changed Kconfig to depend on just OF && HAS_IOMEM
- fixed LLVM warnings on FIELD_PREP and missing slab.h as reported-by <lkp@intel.com>
v3 -> v4
 - avoid magic numbers for regs padding holes
 - renaming various enums terminators to count_ instead of max_
 - using scoped_guards for spinlock_save
 - dropping FIRST_EXT naming for 0-indexed enum
 - reduce indentation by using early returns or continue on failure-paths
 - use dev_err_probe where appropriate
 - be less noisy with dev_dbg
 - refactored mhuv3_mbx_comb_interrupt using __free cleanups for .read_data
 - refactored doorbell lookups with scoped_guards
 - fail on IRQ request failures: do not carry-on best effort
 - drop usage of platform_set_drvdata and .remove in favour of
   devm_add_action_or_reset
 - review failures handling on extensions initialization
 - removed name clashes
 - more comments on regs decorations
 - decreasing line-lengths definitions
 - use __ffs instead of __builtin_ctz
 - dropped used of bitfields in favour of bitmasks
 - reading implementer/revision/variant/product_id
 - fixed a few misspellings
 - DT: using ARM GIC defines in example
 - DT: defined MHUv3 Extensions types in new file dt-bindings/arm/mhuv3-dt.h
v2 -> v3
 - fixed spurious tabs/spaces in DT binding
v1 -> v2
 - clarified DT bindings extension descriptions around configurability
   and discoverability
 - removed unused labels from the DT example
 - using pattern properties to define DT interrupt-names
 - bumped DT interrupt maxItems to 74 (allowing uo to 8 channels per extension)
 - fixed checkpatch warnings about side-effects on write/read bitfield macros
 - fixed sparse errors as reported
   | Reported-by: kernel test robot <lkp@intel.com>
   | Closes: https://lore.kernel.org/oe-kbuild-all/202403290015.tCLXudqC-lkp@intel.com/

Cristian Marussi (2):
  dt-bindings: mailbox: arm,mhuv3: Add bindings
  mailbox: arm_mhuv3: Add driver

 .../bindings/mailbox/arm,mhuv3.yaml           |  224 ++++
 MAINTAINERS                                   |    9 +
 drivers/mailbox/Kconfig                       |   12 +
 drivers/mailbox/Makefile                      |    2 +
 drivers/mailbox/arm_mhuv3.c                   | 1103 +++++++++++++++++
 include/dt-bindings/arm/mhuv3-dt.h            |   13 +
 6 files changed, 1363 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mailbox/arm,mhuv3.yaml
 create mode 100644 drivers/mailbox/arm_mhuv3.c
 create mode 100644 include/dt-bindings/arm/mhuv3-dt.h

-- 
2.34.1


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH v5 0/2] Add initial ARM MHUv3 mailbox support
@ 2024-04-18 10:52 ` Cristian Marussi
  0 siblings, 0 replies; 6+ messages in thread
From: Cristian Marussi @ 2024-04-18 10:52 UTC (permalink / raw)
  To: linux-kernel, linux-arm-kernel, devicetree
  Cc: sudeep.holla, cristian.marussi, jassisinghbrar, robh+dt,
	krzysztof.kozlowski+dt, conor+dt

Hi,

This series adds support for the new ARM Message Handling Unit v3 mailbox
controller [1].

The ARM MHUv3 can optionally support various extensions, enabling the
usage of different transport protocols.

Patch [2/2] adds a platform driver which, as of now, provides support only
for the Doorbell extension using the combined interrupt.

On the other side, bindings in [1/2] are introduced for all the extensions
described by the specification, as long as they are of interest to an
entity running from Normal world, like Linux: as such, Doorbell, FIFO and
FastChannel extensions are documented.

In these regards, note that the ARM MHUv3 controller can optionally
implement a considerable number of interrupts to express a great deal of
events and many of such interrupts are defined as being per-channel: with
the total maximum amount of possibly implemented channels across all
extensions being 1216 (1024+128+64), it would mean *a lot* of
interrupt-names to enumerate in the bindings.

For the sake of simplicity the binding as of now only introduces interrupt
names for a mere 8-channels in the range (0,7) for each per-channel
interrupt type: the idea is to leave open the possibility to add more to
this list of numbered items only when (and if) new real HW appears that
effectively needs more than 8 channels. (like AMBA, where the maximum
number of IRQ was progressively increased when needed, AFAIU).

Based on v6.9-rc1, tested on ARM TCS23 [2]
(TCS23 reference SW stack is still to be made fully publicly available)

Thanks,
Cristian

[1]: https://developer.arm.com/documentation/aes0072/aa/?lang=en
[2]: https://community.arm.com/arm-community-blogs/b/tools-software-ides-blog/posts/total-compute-solutions-platform-software-stack-and-fvp

---
v4 -> v5
- changed Kconfig to depend on just OF && HAS_IOMEM
- fixed LLVM warnings on FIELD_PREP and missing slab.h as reported-by <lkp@intel.com>
v3 -> v4
 - avoid magic numbers for regs padding holes
 - renaming various enums terminators to count_ instead of max_
 - using scoped_guards for spinlock_save
 - dropping FIRST_EXT naming for 0-indexed enum
 - reduce indentation by using early returns or continue on failure-paths
 - use dev_err_probe where appropriate
 - be less noisy with dev_dbg
 - refactored mhuv3_mbx_comb_interrupt using __free cleanups for .read_data
 - refactored doorbell lookups with scoped_guards
 - fail on IRQ request failures: do not carry-on best effort
 - drop usage of platform_set_drvdata and .remove in favour of
   devm_add_action_or_reset
 - review failures handling on extensions initialization
 - removed name clashes
 - more comments on regs decorations
 - decreasing line-lengths definitions
 - use __ffs instead of __builtin_ctz
 - dropped used of bitfields in favour of bitmasks
 - reading implementer/revision/variant/product_id
 - fixed a few misspellings
 - DT: using ARM GIC defines in example
 - DT: defined MHUv3 Extensions types in new file dt-bindings/arm/mhuv3-dt.h
v2 -> v3
 - fixed spurious tabs/spaces in DT binding
v1 -> v2
 - clarified DT bindings extension descriptions around configurability
   and discoverability
 - removed unused labels from the DT example
 - using pattern properties to define DT interrupt-names
 - bumped DT interrupt maxItems to 74 (allowing uo to 8 channels per extension)
 - fixed checkpatch warnings about side-effects on write/read bitfield macros
 - fixed sparse errors as reported
   | Reported-by: kernel test robot <lkp@intel.com>
   | Closes: https://lore.kernel.org/oe-kbuild-all/202403290015.tCLXudqC-lkp@intel.com/

Cristian Marussi (2):
  dt-bindings: mailbox: arm,mhuv3: Add bindings
  mailbox: arm_mhuv3: Add driver

 .../bindings/mailbox/arm,mhuv3.yaml           |  224 ++++
 MAINTAINERS                                   |    9 +
 drivers/mailbox/Kconfig                       |   12 +
 drivers/mailbox/Makefile                      |    2 +
 drivers/mailbox/arm_mhuv3.c                   | 1103 +++++++++++++++++
 include/dt-bindings/arm/mhuv3-dt.h            |   13 +
 6 files changed, 1363 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mailbox/arm,mhuv3.yaml
 create mode 100644 drivers/mailbox/arm_mhuv3.c
 create mode 100644 include/dt-bindings/arm/mhuv3-dt.h

-- 
2.34.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH v5 1/2] dt-bindings: mailbox: arm,mhuv3: Add bindings
  2024-04-18 10:52 ` Cristian Marussi
@ 2024-04-18 10:52   ` Cristian Marussi
  -1 siblings, 0 replies; 6+ messages in thread
From: Cristian Marussi @ 2024-04-18 10:52 UTC (permalink / raw)
  To: linux-kernel, linux-arm-kernel, devicetree
  Cc: sudeep.holla, cristian.marussi, jassisinghbrar, robh+dt,
	krzysztof.kozlowski+dt, conor+dt, Rob Herring

Add bindings for the ARM MHUv3 Mailbox controller.

Reviewed-by: Rob Herring <robh@kernel.org>
Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
---
v3 -> v4
- using ARM GIC defines in example
- defined MHUv3 Extensions types in dt-bindings/arm/mhuv3-dt.h
v2 -> v3
- fixed spurious tabs in dt_binding_check
v1 -> v2
- clarified extension descriptions around configurability and discoverability
- removed unused labels from the example
- using pattern properties to define interrupt-names
- bumped interrupt maxItems to 74 (allowing uo to 8 channels per extension)
---
 .../bindings/mailbox/arm,mhuv3.yaml           | 224 ++++++++++++++++++
 include/dt-bindings/arm/mhuv3-dt.h            |  13 +
 2 files changed, 237 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mailbox/arm,mhuv3.yaml
 create mode 100644 include/dt-bindings/arm/mhuv3-dt.h

diff --git a/Documentation/devicetree/bindings/mailbox/arm,mhuv3.yaml b/Documentation/devicetree/bindings/mailbox/arm,mhuv3.yaml
new file mode 100644
index 000000000000..449b55afeb7d
--- /dev/null
+++ b/Documentation/devicetree/bindings/mailbox/arm,mhuv3.yaml
@@ -0,0 +1,224 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/mailbox/arm,mhuv3.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: ARM MHUv3 Mailbox Controller
+
+maintainers:
+  - Sudeep Holla <sudeep.holla@arm.com>
+  - Cristian Marussi <cristian.marussi@arm.com>
+
+description: |
+  The Arm Message Handling Unit (MHU) Version 3 is a mailbox controller that
+  enables unidirectional communications with remote processors through various
+  possible transport protocols.
+  The controller can optionally support a varying number of extensions that, in
+  turn, enable different kinds of transport to be used for communication.
+  Number, type and characteristics of each supported extension can be discovered
+  dynamically at runtime.
+
+  Given the unidirectional nature of the controller, an MHUv3 mailbox controller
+  is composed of a MHU Sender (MHUS) containing a PostBox (PBX) block and a MHU
+  Receiver (MHUR) containing a MailBox (MBX) block, where
+
+   PBX is used to
+      - Configure the MHU
+      - Send Transfers to the Receiver
+      - Optionally receive acknowledgment of a Transfer from the Receiver
+
+   MBX is used to
+      - Configure the MHU
+      - Receive Transfers from the Sender
+      - Optionally acknowledge Transfers sent by the Sender
+
+  Both PBX and MBX need to be present and defined in the DT description if you
+  need to establish a bidirectional communication, since you will have to
+  acquire two distinct unidirectional channels, one for each block.
+
+  As a consequence both blocks needs to be represented separately and specified
+  as distinct DT nodes in order to properly describe their resources.
+
+  Note that, though, thanks to the runtime discoverability, there is no need to
+  identify the type of blocks with distinct compatibles.
+
+  Following are the MHUv3 possible extensions.
+
+  - Doorbell Extension (DBE): DBE defines a type of channel called a Doorbell
+    Channel (DBCH). DBCH enables a single bit Transfer to be sent from the
+    Sender to Receiver. The Transfer indicates that an event has occurred.
+    When DBE is implemented, the number of DBCHs that an implementation of the
+    MHU can support is between 1 and 128, numbered starting from 0 in ascending
+    order and discoverable at run-time.
+    Each DBCH contains 32 individual fields, referred to as flags, each of which
+    can be used independently. It is possible for the Sender to send multiple
+    Transfers at once using a single DBCH, so long as each Transfer uses
+    a different flag in the DBCH.
+    Optionally, data may be transmitted through an out-of-band shared memory
+    region, wherein the MHU Doorbell is used strictly as an interrupt generation
+    mechanism, but this is out of the scope of these bindings.
+
+  - FastChannel Extension (FCE): FCE defines a type of channel called a Fast
+    Channel (FCH). FCH is intended for lower overhead communication between
+    Sender and Receiver at the expense of determinism. An FCH allows the Sender
+    to update the channel value at any time, regardless of whether the previous
+    value has been seen by the Receiver. When the Receiver reads the channel's
+    content it gets the last value written to the channel.
+    FCH is considered lossy in nature, and means that the Sender has no way of
+    knowing if, or when, the Receiver will act on the Transfer.
+    FCHs are expected to behave as RAM which generates interrupts when writes
+    occur to the locations within the RAM.
+    When FCE is implemented, the number of FCHs that an implementation of the
+    MHU can support is between 1-1024, if the FastChannel word-size is 32-bits,
+    or between 1-512, when the FastChannel word-size is 64-bits.
+    FCHs are numbered from 0 in ascending order.
+    Note that the number of FCHs and the word-size are implementation defined,
+    not configurable but discoverable at run-time.
+    Optionally, data may be transmitted through an out-of-band shared memory
+    region, wherein the MHU FastChannel is used as an interrupt generation
+    mechanism which carries also a pointer to such out-of-band data, but this
+    is out of the scope of these bindings.
+
+  - FIFO Extension (FE): FE defines a Channel type called a FIFO Channel (FFCH).
+    FFCH allows a Sender to send
+       - Multiple Transfers to the Receiver without having to wait for the
+         previous Transfer to be acknowledged by the Receiver, as long as the
+         FIFO has room for the Transfer.
+       - Transfers which require the Receiver to provide acknowledgment.
+       - Transfers which have in-band payload.
+    In all cases, the data is guaranteed to be observed by the Receiver in the
+    same order which the Sender sent it.
+    When FE is implemented, the number of FFCHs that an implementation of the
+    MHU can support is between 1 and 64, numbered starting from 0 in ascending
+    order. The number of FFCHs, their depth (same for all implemented FFCHs) and
+    the access-granularity are implementation defined, not configurable but
+    discoverable at run-time.
+    Optionally, additional data may be transmitted through an out-of-band shared
+    memory region, wherein the MHU FIFO is used to transmit, in order, a small
+    part of the payload (like a header) and a reference to the shared memory
+    area holding the remaining, bigger, chunk of the payload, but this is out of
+    the scope of these bindings.
+
+properties:
+  compatible:
+    const: arm,mhuv3
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    minItems: 1
+    maxItems: 74
+
+  interrupt-names:
+    description: |
+      The MHUv3 controller generates a number of events some of which are used
+      to generate interrupts; as a consequence it can expose a varying number of
+      optional PBX/MBX interrupts, representing the events generated during the
+      operation of the various transport protocols associated with different
+      extensions. All interrupts of the MHU are level-sensitive.
+      Some of these optional interrupts are defined per-channel, where the
+      number of channels effectively available is implementation defined and
+      run-time discoverable.
+      In the following names are enumerated using patterns, with per-channel
+      interrupts implicitly capped at the maximum channels allowed by the
+      specification for each extension type.
+      For the sake of simplicity maxItems is anyway capped to a most plausible
+      number, assuming way less channels would be implemented than actually
+      possible.
+
+      The only mandatory interrupts on the MHU are:
+        - combined
+        - mbx-fch-xfer-<N> but only if mbx-fcgrp-xfer-<N> is not implemented.
+
+    minItems: 1
+    maxItems: 74
+    items:
+      oneOf:
+        - const: combined
+          description: PBX/MBX Combined interrupt
+        - const: combined-ffch
+          description: PBX/MBX FIFO Combined interrupt
+        - pattern: '^ffch-low-tide-[0-9]+$'
+          description: PBX/MBX FIFO Channel <N> Low Tide interrupt
+        - pattern: '^ffch-high-tide-[0-9]+$'
+          description: PBX/MBX FIFO Channel <N> High Tide interrupt
+        - pattern: '^ffch-flush-[0-9]+$'
+          description: PBX/MBX FIFO Channel <N> Flush interrupt
+        - pattern: '^mbx-dbch-xfer-[0-9]+$'
+          description: MBX Doorbell Channel <N> Transfer interrupt
+        - pattern: '^mbx-fch-xfer-[0-9]+$'
+          description: MBX FastChannel <N> Transfer interrupt
+        - pattern: '^mbx-fchgrp-xfer-[0-9]+$'
+          description: MBX FastChannel <N> Group Transfer interrupt
+        - pattern: '^mbx-ffch-xfer-[0-9]+$'
+          description: MBX FIFO Channel <N> Transfer interrupt
+        - pattern: '^pbx-dbch-xfer-ack-[0-9]+$'
+          description: PBX Doorbell Channel <N> Transfer Ack interrupt
+        - pattern: '^pbx-ffch-xfer-ack-[0-9]+$'
+          description: PBX FIFO Channel <N> Transfer Ack interrupt
+
+  '#mbox-cells':
+    description: |
+      The first argument in the consumers 'mboxes' property represents the
+      extension type, the second is for the channel number while the third
+      depends on extension type.
+
+      Extension types constants are defined in <dt-bindings/arm/mhuv3-dt.h>.
+
+      Extension type for DBE is DBE_EXT and the third parameter represents the
+      doorbell flag number to use.
+      Extension type for FCE is FCE_EXT, third parameter unused.
+      Extension type for FE is FE_EXT, third parameter unused.
+
+      mboxes = <&mhu DBE_EXT 0 5>; // DBE, Doorbell Channel Window 0, doorbell 5.
+      mboxes = <&mhu DBE_EXT 7>; // DBE, Doorbell Channel Window 1, doorbell 7.
+      mboxes = <&mhu FCE_EXT 0 0>; // FCE, FastChannel Window 0.
+      mboxes = <&mhu FCE_EXT 3 0>; // FCE, FastChannel Window 3.
+      mboxes = <&mhu FE_EXT 1 0>; // FE, FIFO Channel Window 1.
+      mboxes = <&mhu FE_EXT 7 0>; // FE, FIFO Channel Window 7.
+    const: 3
+
+  clocks:
+    maxItems: 1
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - interrupt-names
+  - '#mbox-cells'
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+
+    soc {
+        #address-cells = <2>;
+        #size-cells = <2>;
+
+        mailbox@2aaa0000 {
+            compatible = "arm,mhuv3";
+            #mbox-cells = <3>;
+            reg = <0 0x2aaa0000 0 0x10000>;
+            clocks = <&clock 0>;
+            interrupt-names = "combined", "pbx-dbch-xfer-ack-1",
+                               "ffch-high-tide-0";
+            interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>,
+                         <GIC_SPI 37 IRQ_TYPE_LEVEL_HIGH>;
+        };
+
+        mailbox@2ab00000 {
+            compatible = "arm,mhuv3";
+            #mbox-cells = <3>;
+            reg = <0 0x2aab0000 0 0x10000>;
+            clocks = <&clock 0>;
+            interrupt-names = "combined", "mbx-dbch-xfer-1", "ffch-low-tide-0";
+            interrupts = <GIC_SPI 35 IRQ_TYPE_LEVEL_HIGH>,
+                         <GIC_SPI 38 IRQ_TYPE_LEVEL_HIGH>,
+                         <GIC_SPI 39 IRQ_TYPE_LEVEL_HIGH>;
+        };
+    };
diff --git a/include/dt-bindings/arm/mhuv3-dt.h b/include/dt-bindings/arm/mhuv3-dt.h
new file mode 100644
index 000000000000..4575406919dd
--- /dev/null
+++ b/include/dt-bindings/arm/mhuv3-dt.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
+/*
+ * This header provides constants for the defined MHUv3 types.
+ */
+
+#ifndef _DT_BINDINGS_ARM_MHUV3_DT_H
+#define _DT_BINDINGS_ARM_MHUV3_DT_H
+
+#define DBE_EXT		0
+#define FCE_EXT		1
+#define FE_EXT		2
+
+#endif /* _DT_BINDINGS_ARM_MHUV3_DT_H */
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH v5 1/2] dt-bindings: mailbox: arm,mhuv3: Add bindings
@ 2024-04-18 10:52   ` Cristian Marussi
  0 siblings, 0 replies; 6+ messages in thread
From: Cristian Marussi @ 2024-04-18 10:52 UTC (permalink / raw)
  To: linux-kernel, linux-arm-kernel, devicetree
  Cc: sudeep.holla, cristian.marussi, jassisinghbrar, robh+dt,
	krzysztof.kozlowski+dt, conor+dt, Rob Herring

Add bindings for the ARM MHUv3 Mailbox controller.

Reviewed-by: Rob Herring <robh@kernel.org>
Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
---
v3 -> v4
- using ARM GIC defines in example
- defined MHUv3 Extensions types in dt-bindings/arm/mhuv3-dt.h
v2 -> v3
- fixed spurious tabs in dt_binding_check
v1 -> v2
- clarified extension descriptions around configurability and discoverability
- removed unused labels from the example
- using pattern properties to define interrupt-names
- bumped interrupt maxItems to 74 (allowing uo to 8 channels per extension)
---
 .../bindings/mailbox/arm,mhuv3.yaml           | 224 ++++++++++++++++++
 include/dt-bindings/arm/mhuv3-dt.h            |  13 +
 2 files changed, 237 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mailbox/arm,mhuv3.yaml
 create mode 100644 include/dt-bindings/arm/mhuv3-dt.h

diff --git a/Documentation/devicetree/bindings/mailbox/arm,mhuv3.yaml b/Documentation/devicetree/bindings/mailbox/arm,mhuv3.yaml
new file mode 100644
index 000000000000..449b55afeb7d
--- /dev/null
+++ b/Documentation/devicetree/bindings/mailbox/arm,mhuv3.yaml
@@ -0,0 +1,224 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/mailbox/arm,mhuv3.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: ARM MHUv3 Mailbox Controller
+
+maintainers:
+  - Sudeep Holla <sudeep.holla@arm.com>
+  - Cristian Marussi <cristian.marussi@arm.com>
+
+description: |
+  The Arm Message Handling Unit (MHU) Version 3 is a mailbox controller that
+  enables unidirectional communications with remote processors through various
+  possible transport protocols.
+  The controller can optionally support a varying number of extensions that, in
+  turn, enable different kinds of transport to be used for communication.
+  Number, type and characteristics of each supported extension can be discovered
+  dynamically at runtime.
+
+  Given the unidirectional nature of the controller, an MHUv3 mailbox controller
+  is composed of a MHU Sender (MHUS) containing a PostBox (PBX) block and a MHU
+  Receiver (MHUR) containing a MailBox (MBX) block, where
+
+   PBX is used to
+      - Configure the MHU
+      - Send Transfers to the Receiver
+      - Optionally receive acknowledgment of a Transfer from the Receiver
+
+   MBX is used to
+      - Configure the MHU
+      - Receive Transfers from the Sender
+      - Optionally acknowledge Transfers sent by the Sender
+
+  Both PBX and MBX need to be present and defined in the DT description if you
+  need to establish a bidirectional communication, since you will have to
+  acquire two distinct unidirectional channels, one for each block.
+
+  As a consequence both blocks needs to be represented separately and specified
+  as distinct DT nodes in order to properly describe their resources.
+
+  Note that, though, thanks to the runtime discoverability, there is no need to
+  identify the type of blocks with distinct compatibles.
+
+  Following are the MHUv3 possible extensions.
+
+  - Doorbell Extension (DBE): DBE defines a type of channel called a Doorbell
+    Channel (DBCH). DBCH enables a single bit Transfer to be sent from the
+    Sender to Receiver. The Transfer indicates that an event has occurred.
+    When DBE is implemented, the number of DBCHs that an implementation of the
+    MHU can support is between 1 and 128, numbered starting from 0 in ascending
+    order and discoverable at run-time.
+    Each DBCH contains 32 individual fields, referred to as flags, each of which
+    can be used independently. It is possible for the Sender to send multiple
+    Transfers at once using a single DBCH, so long as each Transfer uses
+    a different flag in the DBCH.
+    Optionally, data may be transmitted through an out-of-band shared memory
+    region, wherein the MHU Doorbell is used strictly as an interrupt generation
+    mechanism, but this is out of the scope of these bindings.
+
+  - FastChannel Extension (FCE): FCE defines a type of channel called a Fast
+    Channel (FCH). FCH is intended for lower overhead communication between
+    Sender and Receiver at the expense of determinism. An FCH allows the Sender
+    to update the channel value at any time, regardless of whether the previous
+    value has been seen by the Receiver. When the Receiver reads the channel's
+    content it gets the last value written to the channel.
+    FCH is considered lossy in nature, and means that the Sender has no way of
+    knowing if, or when, the Receiver will act on the Transfer.
+    FCHs are expected to behave as RAM which generates interrupts when writes
+    occur to the locations within the RAM.
+    When FCE is implemented, the number of FCHs that an implementation of the
+    MHU can support is between 1-1024, if the FastChannel word-size is 32-bits,
+    or between 1-512, when the FastChannel word-size is 64-bits.
+    FCHs are numbered from 0 in ascending order.
+    Note that the number of FCHs and the word-size are implementation defined,
+    not configurable but discoverable at run-time.
+    Optionally, data may be transmitted through an out-of-band shared memory
+    region, wherein the MHU FastChannel is used as an interrupt generation
+    mechanism which carries also a pointer to such out-of-band data, but this
+    is out of the scope of these bindings.
+
+  - FIFO Extension (FE): FE defines a Channel type called a FIFO Channel (FFCH).
+    FFCH allows a Sender to send
+       - Multiple Transfers to the Receiver without having to wait for the
+         previous Transfer to be acknowledged by the Receiver, as long as the
+         FIFO has room for the Transfer.
+       - Transfers which require the Receiver to provide acknowledgment.
+       - Transfers which have in-band payload.
+    In all cases, the data is guaranteed to be observed by the Receiver in the
+    same order which the Sender sent it.
+    When FE is implemented, the number of FFCHs that an implementation of the
+    MHU can support is between 1 and 64, numbered starting from 0 in ascending
+    order. The number of FFCHs, their depth (same for all implemented FFCHs) and
+    the access-granularity are implementation defined, not configurable but
+    discoverable at run-time.
+    Optionally, additional data may be transmitted through an out-of-band shared
+    memory region, wherein the MHU FIFO is used to transmit, in order, a small
+    part of the payload (like a header) and a reference to the shared memory
+    area holding the remaining, bigger, chunk of the payload, but this is out of
+    the scope of these bindings.
+
+properties:
+  compatible:
+    const: arm,mhuv3
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    minItems: 1
+    maxItems: 74
+
+  interrupt-names:
+    description: |
+      The MHUv3 controller generates a number of events some of which are used
+      to generate interrupts; as a consequence it can expose a varying number of
+      optional PBX/MBX interrupts, representing the events generated during the
+      operation of the various transport protocols associated with different
+      extensions. All interrupts of the MHU are level-sensitive.
+      Some of these optional interrupts are defined per-channel, where the
+      number of channels effectively available is implementation defined and
+      run-time discoverable.
+      In the following names are enumerated using patterns, with per-channel
+      interrupts implicitly capped at the maximum channels allowed by the
+      specification for each extension type.
+      For the sake of simplicity maxItems is anyway capped to a most plausible
+      number, assuming way less channels would be implemented than actually
+      possible.
+
+      The only mandatory interrupts on the MHU are:
+        - combined
+        - mbx-fch-xfer-<N> but only if mbx-fcgrp-xfer-<N> is not implemented.
+
+    minItems: 1
+    maxItems: 74
+    items:
+      oneOf:
+        - const: combined
+          description: PBX/MBX Combined interrupt
+        - const: combined-ffch
+          description: PBX/MBX FIFO Combined interrupt
+        - pattern: '^ffch-low-tide-[0-9]+$'
+          description: PBX/MBX FIFO Channel <N> Low Tide interrupt
+        - pattern: '^ffch-high-tide-[0-9]+$'
+          description: PBX/MBX FIFO Channel <N> High Tide interrupt
+        - pattern: '^ffch-flush-[0-9]+$'
+          description: PBX/MBX FIFO Channel <N> Flush interrupt
+        - pattern: '^mbx-dbch-xfer-[0-9]+$'
+          description: MBX Doorbell Channel <N> Transfer interrupt
+        - pattern: '^mbx-fch-xfer-[0-9]+$'
+          description: MBX FastChannel <N> Transfer interrupt
+        - pattern: '^mbx-fchgrp-xfer-[0-9]+$'
+          description: MBX FastChannel <N> Group Transfer interrupt
+        - pattern: '^mbx-ffch-xfer-[0-9]+$'
+          description: MBX FIFO Channel <N> Transfer interrupt
+        - pattern: '^pbx-dbch-xfer-ack-[0-9]+$'
+          description: PBX Doorbell Channel <N> Transfer Ack interrupt
+        - pattern: '^pbx-ffch-xfer-ack-[0-9]+$'
+          description: PBX FIFO Channel <N> Transfer Ack interrupt
+
+  '#mbox-cells':
+    description: |
+      The first argument in the consumers 'mboxes' property represents the
+      extension type, the second is for the channel number while the third
+      depends on extension type.
+
+      Extension types constants are defined in <dt-bindings/arm/mhuv3-dt.h>.
+
+      Extension type for DBE is DBE_EXT and the third parameter represents the
+      doorbell flag number to use.
+      Extension type for FCE is FCE_EXT, third parameter unused.
+      Extension type for FE is FE_EXT, third parameter unused.
+
+      mboxes = <&mhu DBE_EXT 0 5>; // DBE, Doorbell Channel Window 0, doorbell 5.
+      mboxes = <&mhu DBE_EXT 7>; // DBE, Doorbell Channel Window 1, doorbell 7.
+      mboxes = <&mhu FCE_EXT 0 0>; // FCE, FastChannel Window 0.
+      mboxes = <&mhu FCE_EXT 3 0>; // FCE, FastChannel Window 3.
+      mboxes = <&mhu FE_EXT 1 0>; // FE, FIFO Channel Window 1.
+      mboxes = <&mhu FE_EXT 7 0>; // FE, FIFO Channel Window 7.
+    const: 3
+
+  clocks:
+    maxItems: 1
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - interrupt-names
+  - '#mbox-cells'
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+
+    soc {
+        #address-cells = <2>;
+        #size-cells = <2>;
+
+        mailbox@2aaa0000 {
+            compatible = "arm,mhuv3";
+            #mbox-cells = <3>;
+            reg = <0 0x2aaa0000 0 0x10000>;
+            clocks = <&clock 0>;
+            interrupt-names = "combined", "pbx-dbch-xfer-ack-1",
+                               "ffch-high-tide-0";
+            interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>,
+                         <GIC_SPI 37 IRQ_TYPE_LEVEL_HIGH>;
+        };
+
+        mailbox@2ab00000 {
+            compatible = "arm,mhuv3";
+            #mbox-cells = <3>;
+            reg = <0 0x2aab0000 0 0x10000>;
+            clocks = <&clock 0>;
+            interrupt-names = "combined", "mbx-dbch-xfer-1", "ffch-low-tide-0";
+            interrupts = <GIC_SPI 35 IRQ_TYPE_LEVEL_HIGH>,
+                         <GIC_SPI 38 IRQ_TYPE_LEVEL_HIGH>,
+                         <GIC_SPI 39 IRQ_TYPE_LEVEL_HIGH>;
+        };
+    };
diff --git a/include/dt-bindings/arm/mhuv3-dt.h b/include/dt-bindings/arm/mhuv3-dt.h
new file mode 100644
index 000000000000..4575406919dd
--- /dev/null
+++ b/include/dt-bindings/arm/mhuv3-dt.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
+/*
+ * This header provides constants for the defined MHUv3 types.
+ */
+
+#ifndef _DT_BINDINGS_ARM_MHUV3_DT_H
+#define _DT_BINDINGS_ARM_MHUV3_DT_H
+
+#define DBE_EXT		0
+#define FCE_EXT		1
+#define FE_EXT		2
+
+#endif /* _DT_BINDINGS_ARM_MHUV3_DT_H */
-- 
2.34.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH v5 2/2] mailbox: arm_mhuv3: Add driver
  2024-04-18 10:52 ` Cristian Marussi
@ 2024-04-18 10:52   ` Cristian Marussi
  -1 siblings, 0 replies; 6+ messages in thread
From: Cristian Marussi @ 2024-04-18 10:52 UTC (permalink / raw)
  To: linux-kernel, linux-arm-kernel, devicetree
  Cc: sudeep.holla, cristian.marussi, jassisinghbrar, robh+dt,
	krzysztof.kozlowski+dt, conor+dt

Add support for ARM MHUv3 mailbox controller.

Support is limited to the MHUv3 Doorbell extension using only the PBX/MBX
combined interrupts.

Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
---
v4 -> v5
- changed Kconfig depends on to just need oF && HAS_IOMEM
- fixed LLVM warnings on FIELD_PREP as reported-by <lkp@intel.com>

v3 -> v4
- avoid magic numbers for padding holes
- renaming various enums terminators to count_ instead of max_
- using scoped_guards for spinlock_save
- dropping FIRST_EXT naming for 0-indexed enum
- less indentation by using early returns or continue on failure-paths
- use dev_err_probe where appropriate
- less noisy with dev_dbg
- refactored mhuv3_mbx_comb_interrupt using __free cleanups for .read_data
- refactored doorbell lookups with scoped_guards
- fail on IRQ request failures: not carrying-on best effort
- drop usage of platform_set_drvdata and .remove in favour of
  devm_add_action_or_reset
- review failures handling on extensions initialization
- removed name clashes
- more comments on regs decorations
- decreasing line-lengths definitions
- use __ffs instead of __builtin_ctz
- dropped used of bitfields in favour of bitmasks
- reading implementer/revision/variant/product_id
- fixed misspellings

v1 -> v2
- fixed checkpatch warnings about side-effects
- fixed sparse errors as reported
  | Reported-by: kernel test robot <lkp@intel.com>
  | Closes: https://lore.kernel.org/oe-kbuild-all/202403290015.tCLXudqC-lkp@intel.com/
---
 MAINTAINERS                 |    9 +
 drivers/mailbox/Kconfig     |   12 +
 drivers/mailbox/Makefile    |    2 +
 drivers/mailbox/arm_mhuv3.c | 1103 +++++++++++++++++++++++++++++++++++
 4 files changed, 1126 insertions(+)
 create mode 100644 drivers/mailbox/arm_mhuv3.c

diff --git a/MAINTAINERS b/MAINTAINERS
index aa3b947fb080..e957b9d9e32a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -12998,6 +12998,15 @@ F:	Documentation/devicetree/bindings/mailbox/arm,mhuv2.yaml
 F:	drivers/mailbox/arm_mhuv2.c
 F:	include/linux/mailbox/arm_mhuv2_message.h
 
+MAILBOX ARM MHUv3
+M:	Sudeep Holla <sudeep.holla@arm.com>
+M:	Cristian Marussi <cristian.marussi@arm.com>
+L:	linux-kernel@vger.kernel.org
+L:	linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S:	Maintained
+F:	Documentation/devicetree/bindings/mailbox/arm,mhuv3.yaml
+F:	drivers/mailbox/arm_mhuv3.c
+
 MAN-PAGES: MANUAL PAGES FOR LINUX -- Sections 2, 3, 4, 5, and 7
 M:	Alejandro Colomar <alx@kernel.org>
 L:	linux-man@vger.kernel.org
diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig
index 42940108a187..cce8c33688fa 100644
--- a/drivers/mailbox/Kconfig
+++ b/drivers/mailbox/Kconfig
@@ -23,6 +23,18 @@ config ARM_MHU_V2
 	  Say Y here if you want to build the ARM MHUv2 controller driver,
 	  which provides unidirectional mailboxes between processing elements.
 
+config ARM_MHU_V3
+	tristate "ARM MHUv3 Mailbox"
+	depends on HAS_IOMEM || COMPILE_TEST
+	depends on OF
+	help
+	  Say Y here if you want to build the ARM MHUv3 controller driver,
+	  which provides unidirectional mailboxes between processing elements.
+
+	  ARM MHUv3 controllers can implement a varying number of extensions
+	  that provides different means of transports: supported extensions
+	  will be discovered and possibly managed at probe-time.
+
 config IMX_MBOX
 	tristate "i.MX Mailbox"
 	depends on ARCH_MXC || COMPILE_TEST
diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile
index 18793e6caa2f..5cf2f54debaf 100644
--- a/drivers/mailbox/Makefile
+++ b/drivers/mailbox/Makefile
@@ -9,6 +9,8 @@ obj-$(CONFIG_ARM_MHU)	+= arm_mhu.o arm_mhu_db.o
 
 obj-$(CONFIG_ARM_MHU_V2)	+= arm_mhuv2.o
 
+obj-$(CONFIG_ARM_MHU_V3)	+= arm_mhuv3.o
+
 obj-$(CONFIG_IMX_MBOX)	+= imx-mailbox.o
 
 obj-$(CONFIG_ARMADA_37XX_RWTM_MBOX)	+= armada-37xx-rwtm-mailbox.o
diff --git a/drivers/mailbox/arm_mhuv3.c b/drivers/mailbox/arm_mhuv3.c
new file mode 100644
index 000000000000..b97e79a5870f
--- /dev/null
+++ b/drivers/mailbox/arm_mhuv3.c
@@ -0,0 +1,1103 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ARM Message Handling Unit Version 3 (MHUv3) driver.
+ *
+ * Copyright (C) 2024 ARM Ltd.
+ *
+ * Based on ARM MHUv2 driver.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/bits.h>
+#include <linux/cleanup.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/mailbox_controller.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/sizes.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+/* ====== MHUv3 Registers ====== */
+
+/* Maximum number of Doorbell channel windows */
+#define MHUV3_DBCW_MAX			128
+/* Number of DBCH combined interrupt status registers */
+#define MHUV3_DBCH_CMB_INT_ST_REG_CNT	4
+
+/* Number of FFCH combined interrupt status registers */
+#define MHUV3_FFCH_CMB_INT_ST_REG_CNT	2
+
+#define MHUV3_FLAG_BITS			32
+
+/* Not a typo ... */
+#define MHUV3_MAJOR_VERSION		2
+
+enum {
+	MHUV3_MBOX_CELL_TYPE,
+	MHUV3_MBOX_CELL_CHWN,
+	MHUV3_MBOX_CELL_PARAM,
+	MHUV3_MBOX_CELLS
+};
+
+/* Padding bitfields/fields represents hole in the regs MMIO */
+
+/* CTRL_Page */
+struct blk_id {
+#define id		GENMASK(3, 0)
+	u32 val;
+} __packed;
+
+struct feat_spt0 {
+#define	dbe_spt		GENMASK(3, 0)
+#define	fe_spt		GENMASK(7, 4)
+#define	fce_spt		GENMASK(11, 8)
+	u32 val;
+} __packed;
+
+struct feat_spt1 {
+#define	auto_op_spt	GENMASK(3, 0)
+	u32 val;
+} __packed;
+
+struct dbch_cfg0 {
+#define num_dbch	GENMASK(7, 0)
+	u32 val;
+} __packed;
+
+struct ffch_cfg0 {
+#define num_ffch	GENMASK(7, 0)
+#define x8ba_spt	BIT(8)
+#define x16ba_spt	BIT(9)
+#define x32ba_spt	BIT(10)
+#define x64ba_spt	BIT(11)
+#define ffch_depth	GENMASK(25, 16)
+	u32 val;
+} __packed;
+
+struct fch_cfg0 {
+#define num_fch		GENMASK(9, 0)
+#define fcgi_spt	BIT(10)		// MBX-only
+#define num_fcg		GENMASK(15, 11)
+#define num_fch_per_grp	GENMASK(20, 16)
+#define fch_ws		GENMASK(28, 21)
+	u32 val;
+} __packed;
+
+struct ctrl {
+#define op_req		BIT(0)
+#define	ch_op_mask	BIT(1)
+	u32 val;
+} __packed;
+
+struct fch_ctrl {
+#define _int_en		BIT(2)
+	u32 val;
+} __packed;
+
+struct iidr {
+#define implementer	GENMASK(11, 0)
+#define revision	GENMASK(15, 12)
+#define variant		GENMASK(19, 16)
+#define product_id	GENMASK(31, 20)
+	u32 val;
+} __packed;
+
+struct aidr {
+#define arch_minor_rev	GENMASK(3, 0)
+#define arch_major_rev	GENMASK(7, 4)
+	u32 val;
+} __packed;
+
+struct ctrl_page {
+	struct blk_id blk_id;
+	u8 pad[12];
+	struct feat_spt0 feat_spt0;
+	struct feat_spt1 feat_spt1;
+	u8 pad1[8];
+	struct dbch_cfg0 dbch_cfg0;
+	u8 pad2[12];
+	struct ffch_cfg0 ffch_cfg0;
+	u8 pad3[12];
+	struct fch_cfg0 fch_cfg0;
+	u8 pad4[188];
+	struct ctrl x_ctrl;
+	/*-- MBX-only registers --*/
+	u8 pad5[60];
+	struct fch_ctrl fch_ctrl;
+	u32 fcg_int_en;
+	u8 pad6[696];
+	/*-- End of MBX-only ---- */
+	u32 dbch_int_st[MHUV3_DBCH_CMB_INT_ST_REG_CNT];
+	u32 ffch_int_st[MHUV3_FFCH_CMB_INT_ST_REG_CNT];
+	/*-- MBX-only registers --*/
+	u8 pad7[88];
+	u32 fcg_int_st;
+	u8 pad8[12];
+	u32 fcg_grp_int_st[32];
+	u8 pad9[2760];
+	/*-- End of MBX-only ---- */
+	struct iidr iidr;
+	struct aidr aidr;
+	u32 imp_def_id[12];
+} __packed;
+
+/* DBCW_Page */
+
+struct xbcw_ctrl {
+#define comb_en		BIT(0)
+	u32 val;
+} __packed;
+
+struct pdbcw_int {
+#define tfr_ack		BIT(0)
+	u32 val;
+} __packed;
+
+struct pdbcw_page {
+	u32 st;
+	u8 pad[8];
+	u32 set;
+	struct pdbcw_int int_st;
+	struct pdbcw_int int_clr;
+	struct pdbcw_int int_en;
+	struct xbcw_ctrl ctrl;
+} __packed;
+
+struct mdbcw_page {
+	u32 st;
+	u32 st_msk;
+	u32 clr;
+	u8 pad[4];
+	u32 msk_st;
+	u32 msk_set;
+	u32 msk_clr;
+	struct xbcw_ctrl ctrl;
+} __packed;
+
+struct dummy_page {
+	u8 pad[SZ_4K];
+} __packed;
+
+struct mhu3_pbx_frame_reg {
+	struct ctrl_page ctrl;
+	struct pdbcw_page dbcw[MHUV3_DBCW_MAX];
+	struct dummy_page ffcw;
+	struct dummy_page fcw;
+	u8 pad[SZ_4K * 11];
+	struct dummy_page impdef;
+} __packed;
+
+struct mhu3_mbx_frame_reg {
+	struct ctrl_page ctrl;
+	struct mdbcw_page dbcw[MHUV3_DBCW_MAX];
+	struct dummy_page ffcw;
+	struct dummy_page fcw;
+	u8 pad[SZ_4K * 11];
+	struct dummy_page impdef;
+} __packed;
+
+/* Macro for reading a bitmask within a physically mapped packed struct */
+#define readl_relaxed_bitmask(_regptr, _bitmask)			\
+	({								\
+		unsigned long _rval;					\
+		_rval = readl_relaxed(_regptr);				\
+		FIELD_GET(_bitmask, _rval);				\
+	})
+
+/* Macro for writing a bitmask within a physically mapped packed struct */
+#define writel_relaxed_bitmask(_value, _regptr, _bitmask)		\
+	({								\
+		unsigned long _rval;					\
+		typeof(_regptr) _rptr = _regptr;			\
+		typeof(_bitmask) _bmask = _bitmask;			\
+		_rval = readl_relaxed(_rptr);				\
+		_rval &= ~(_bmask);					\
+		_rval |= FIELD_PREP((unsigned long long)_bmask, _value);\
+		writel_relaxed(_rval, _rptr);				\
+	})
+
+/* ====== MHUv3 data structures ====== */
+
+enum mhuv3_frame {
+	PBX_FRAME,
+	MBX_FRAME,
+};
+
+static char *mhuv3_str[] = {
+	"PBX",
+	"MBX"
+};
+
+enum mhuv3_extension_type {
+	DBE_EXT,
+	FCE_EXT,
+	FE_EXT,
+	NUM_EXT
+};
+
+static char *mhuv3_ext_str[] = {
+	"DBE",
+	"FCE",
+	"FE"
+};
+
+struct mhuv3;
+
+/**
+ * struct mhuv3_protocol_ops - MHUv3 operations
+ *
+ * @rx_startup: Receiver startup callback.
+ * @rx_shutdown: Receiver shutdown callback.
+ * @read_data: Read available Sender in-band LE data (if any).
+ * @rx_complete: Acknowledge data reception to the Sender. Any out-of-band data
+ *		 has to have been already retrieved before calling this.
+ * @tx_startup: Sender startup callback.
+ * @tx_shutdown: Sender shutdown callback.
+ * @last_tx_done: Report back to the Sender if the last transfer has completed.
+ * @send_data: Send data to the receiver.
+ *
+ * Each supported transport protocol provides its own implementation of
+ * these operations.
+ */
+struct mhuv3_protocol_ops {
+	int (*rx_startup)(struct mhuv3 *mhu, struct mbox_chan *chan);
+	void (*rx_shutdown)(struct mhuv3 *mhu, struct mbox_chan *chan);
+	void *(*read_data)(struct mhuv3 *mhu, struct mbox_chan *chan);
+	void (*rx_complete)(struct mhuv3 *mhu, struct mbox_chan *chan);
+	void (*tx_startup)(struct mhuv3 *mhu, struct mbox_chan *chan);
+	void (*tx_shutdown)(struct mhuv3 *mhu, struct mbox_chan *chan);
+	int (*last_tx_done)(struct mhuv3 *mhu, struct mbox_chan *chan);
+	int (*send_data)(struct mhuv3 *mhu, struct mbox_chan *chan, void *arg);
+};
+
+/**
+ * struct mhuv3_mbox_chan_priv - MHUv3 channel private information
+ *
+ * @ch_idx: Channel window index associated to this mailbox channel.
+ * @doorbell: Doorbell bit number within the @ch_idx window.
+ *	      Only relevant to Doorbell transport.
+ * @ops: Transport protocol specific operations for this channel.
+ *
+ * Transport specific data attached to mmailbox channel priv data.
+ */
+struct mhuv3_mbox_chan_priv {
+	u32 ch_idx;
+	u32 doorbell;
+	const struct mhuv3_protocol_ops *ops;
+};
+
+/**
+ * struct mhuv3_extension - MHUv3 extension descriptor
+ *
+ * @type: Type of extension
+ * @num_chans: Max number of channels found for this extension.
+ * @base_ch_idx: First channel number assigned to this extension, picked from
+ *		 the set of all mailbox channels descriptors created.
+ * @mbox_of_xlate: Extension specific helper to parse DT and lookup associated
+ *		   channel from the related 'mboxes' property.
+ * @combined_irq_setup: Extension specific helper to setup the combined irq.
+ * @channels_init: Extension specific helper to initialize channels.
+ * @chan_from_comb_irq_get: Extension specific helper to lookup which channel
+ *			    triggered the combined irq.
+ * @pending_db: Array of per-channel pending doorbells.
+ * @pending_lock: Protect access to pending_db.
+ */
+struct mhuv3_extension {
+	enum mhuv3_extension_type type;
+	unsigned int num_chans;
+	unsigned int base_ch_idx;
+	struct mbox_chan *(*mbox_of_xlate)(struct mhuv3 *mhu,
+					   unsigned int channel,
+					   unsigned int param);
+	void (*combined_irq_setup)(struct mhuv3 *mhu);
+	int (*channels_init)(struct mhuv3 *mhu);
+	struct mbox_chan *(*chan_from_comb_irq_get)(struct mhuv3 *mhu);
+	u32 pending_db[MHUV3_DBCW_MAX];
+	/* Protect access to pending_db */
+	spinlock_t pending_lock;
+};
+
+/**
+ * struct mhuv3 - MHUv3 mailbox controller data
+ *
+ * @frame:	Frame type: MBX_FRAME or PBX_FRAME.
+ * @auto_op_full: Flag to indicate if the MHU supports AutoOp full mode.
+ * @major: MHUv3 controller architectural major version.
+ * @minor: MHUv3 controller architectural minor version.
+ * @implem: MHUv3 controller IIDR implementer.
+ * @rev: MHUv3 controller IIDR revision.
+ * @var: MHUv3 controller IIDR variant.
+ * @prod_id: MHUv3 controller IIDR product_id.
+ * @num_chans: The total number of channnels discovered across all extensions.
+ * @cmb_irq: Combined IRQ number if any found defined.
+ * @ctrl: A reference to the MHUv3 control page for this block.
+ * @pbx: Base address of the PBX register mapping region.
+ * @mbx: Base address of the MBX register mapping region.
+ * @ext: Array holding descriptors for any found implemented extension.
+ * @mbox: Mailbox controller belonging to the MHU frame.
+ */
+struct mhuv3 {
+	enum mhuv3_frame frame;
+	bool auto_op_full;
+	unsigned int major;
+	unsigned int minor;
+	unsigned int implem;
+	unsigned int rev;
+	unsigned int var;
+	unsigned int prod_id;
+	unsigned int num_chans;
+	int cmb_irq;
+	struct ctrl_page __iomem *ctrl;
+	union {
+		struct mhu3_pbx_frame_reg __iomem *pbx;
+		struct mhu3_mbx_frame_reg __iomem *mbx;
+	};
+	struct mhuv3_extension *ext[NUM_EXT];
+	struct mbox_controller mbox;
+};
+
+#define mhu_from_mbox(_mbox) container_of(_mbox, struct mhuv3, mbox)
+
+typedef int (*mhuv3_extension_initializer)(struct mhuv3 *mhu);
+
+/* =================== Doorbell transport protocol operations =============== */
+
+static void mhuv3_doorbell_tx_startup(struct mhuv3 *mhu, struct mbox_chan *chan)
+{
+	struct mhuv3_mbox_chan_priv *priv = chan->con_priv;
+
+	/* Enable Transfer Acknowledgment events */
+	writel_relaxed_bitmask(0x1, &mhu->pbx->dbcw[priv->ch_idx].int_en, tfr_ack);
+}
+
+static void mhuv3_doorbell_tx_shutdown(struct mhuv3 *mhu, struct mbox_chan *chan)
+{
+	struct mhuv3_mbox_chan_priv *priv = chan->con_priv;
+	struct mhuv3_extension *e = mhu->ext[DBE_EXT];
+	unsigned long flags;
+
+	/* Disable Channel Transfer Ack events */
+	writel_relaxed_bitmask(0x0, &mhu->pbx->dbcw[priv->ch_idx].int_en, tfr_ack);
+
+	/* Clear Channel Transfer Ack and pending doorbells */
+	writel_relaxed_bitmask(0x1, &mhu->pbx->dbcw[priv->ch_idx].int_clr, tfr_ack);
+	spin_lock_irqsave(&e->pending_lock, flags);
+	e->pending_db[priv->ch_idx] = 0;
+	spin_unlock_irqrestore(&e->pending_lock, flags);
+}
+
+static int mhuv3_doorbell_rx_startup(struct mhuv3 *mhu, struct mbox_chan *chan)
+{
+	struct mhuv3_mbox_chan_priv *priv = chan->con_priv;
+
+	/* Unmask Channel Transfer events */
+	writel_relaxed(BIT(priv->doorbell), &mhu->mbx->dbcw[priv->ch_idx].msk_clr);
+
+	return 0;
+}
+
+static void mhuv3_doorbell_rx_shutdown(struct mhuv3 *mhu,
+				       struct mbox_chan *chan)
+{
+	struct mhuv3_mbox_chan_priv *priv = chan->con_priv;
+
+	/* Mask Channel Transfer events */
+	writel_relaxed(BIT(priv->doorbell), &mhu->mbx->dbcw[priv->ch_idx].msk_set);
+}
+
+static void mhuv3_doorbell_rx_complete(struct mhuv3 *mhu, struct mbox_chan *chan)
+{
+	struct mhuv3_mbox_chan_priv *priv = chan->con_priv;
+
+	/* Clearing the pending transfer generates the Channel Transfer Ack */
+	writel_relaxed(BIT(priv->doorbell), &mhu->mbx->dbcw[priv->ch_idx].clr);
+}
+
+static int mhuv3_doorbell_last_tx_done(struct mhuv3 *mhu,
+				       struct mbox_chan *chan)
+{
+	struct mhuv3_mbox_chan_priv *priv = chan->con_priv;
+	int done;
+
+	done = !(readl_relaxed(&mhu->pbx->dbcw[priv->ch_idx].st) &
+		 BIT(priv->doorbell));
+	if (done) {
+		struct mhuv3_extension *e = mhu->ext[DBE_EXT];
+		unsigned long flags;
+
+		/* Take care to clear the pending doorbell also when polling */
+		spin_lock_irqsave(&e->pending_lock, flags);
+		e->pending_db[priv->ch_idx] &= ~BIT(priv->doorbell);
+		spin_unlock_irqrestore(&e->pending_lock, flags);
+	}
+
+	return done;
+}
+
+static int mhuv3_doorbell_send_data(struct mhuv3 *mhu, struct mbox_chan *chan,
+				    void *arg)
+{
+	struct mhuv3_mbox_chan_priv *priv = chan->con_priv;
+	struct mhuv3_extension *e = mhu->ext[DBE_EXT];
+
+	scoped_guard(spinlock_irqsave, &e->pending_lock) {
+		/* Only one in-flight Transfer is allowed per-doorbell */
+		if (e->pending_db[priv->ch_idx] & BIT(priv->doorbell))
+			return -EBUSY;
+
+		e->pending_db[priv->ch_idx] |= BIT(priv->doorbell);
+	}
+
+	writel_relaxed(BIT(priv->doorbell), &mhu->pbx->dbcw[priv->ch_idx].set);
+
+	return 0;
+}
+
+static const struct mhuv3_protocol_ops mhuv3_doorbell_ops = {
+	.tx_startup = mhuv3_doorbell_tx_startup,
+	.tx_shutdown = mhuv3_doorbell_tx_shutdown,
+	.rx_startup = mhuv3_doorbell_rx_startup,
+	.rx_shutdown = mhuv3_doorbell_rx_shutdown,
+	.rx_complete = mhuv3_doorbell_rx_complete,
+	.last_tx_done = mhuv3_doorbell_last_tx_done,
+	.send_data = mhuv3_doorbell_send_data,
+};
+
+/* Sender and receiver mailbox ops */
+static bool mhuv3_sender_last_tx_done(struct mbox_chan *chan)
+{
+	struct mhuv3_mbox_chan_priv *priv = chan->con_priv;
+	struct mhuv3 *mhu = mhu_from_mbox(chan->mbox);
+
+	return priv->ops->last_tx_done(mhu, chan);
+}
+
+static int mhuv3_sender_send_data(struct mbox_chan *chan, void *data)
+{
+	struct mhuv3_mbox_chan_priv *priv = chan->con_priv;
+	struct mhuv3 *mhu = mhu_from_mbox(chan->mbox);
+
+	if (!priv->ops->last_tx_done(mhu, chan))
+		return -EBUSY;
+
+	return priv->ops->send_data(mhu, chan, data);
+}
+
+static int mhuv3_sender_startup(struct mbox_chan *chan)
+{
+	struct mhuv3_mbox_chan_priv *priv = chan->con_priv;
+	struct mhuv3 *mhu = mhu_from_mbox(chan->mbox);
+
+	if (priv->ops->tx_startup)
+		priv->ops->tx_startup(mhu, chan);
+
+	return 0;
+}
+
+static void mhuv3_sender_shutdown(struct mbox_chan *chan)
+{
+	struct mhuv3_mbox_chan_priv *priv = chan->con_priv;
+	struct mhuv3 *mhu = mhu_from_mbox(chan->mbox);
+
+	if (priv->ops->tx_shutdown)
+		priv->ops->tx_shutdown(mhu, chan);
+}
+
+static const struct mbox_chan_ops mhuv3_sender_ops = {
+	.send_data = mhuv3_sender_send_data,
+	.startup = mhuv3_sender_startup,
+	.shutdown = mhuv3_sender_shutdown,
+	.last_tx_done = mhuv3_sender_last_tx_done,
+};
+
+static int mhuv3_receiver_startup(struct mbox_chan *chan)
+{
+	struct mhuv3_mbox_chan_priv *priv = chan->con_priv;
+	struct mhuv3 *mhu = mhu_from_mbox(chan->mbox);
+
+	return priv->ops->rx_startup(mhu, chan);
+}
+
+static void mhuv3_receiver_shutdown(struct mbox_chan *chan)
+{
+	struct mhuv3_mbox_chan_priv *priv = chan->con_priv;
+	struct mhuv3 *mhu = mhu_from_mbox(chan->mbox);
+
+	priv->ops->rx_shutdown(mhu, chan);
+}
+
+static int mhuv3_receiver_send_data(struct mbox_chan *chan, void *data)
+{
+	dev_err(chan->mbox->dev,
+		"Trying to transmit on a MBX MHUv3 frame\n");
+	return -EIO;
+}
+
+static bool mhuv3_receiver_last_tx_done(struct mbox_chan *chan)
+{
+	dev_err(chan->mbox->dev, "Trying to Tx poll on a MBX MHUv3 frame\n");
+	return true;
+}
+
+static const struct mbox_chan_ops mhuv3_receiver_ops = {
+	.send_data = mhuv3_receiver_send_data,
+	.startup = mhuv3_receiver_startup,
+	.shutdown = mhuv3_receiver_shutdown,
+	.last_tx_done = mhuv3_receiver_last_tx_done,
+};
+
+static struct mbox_chan *mhuv3_dbe_mbox_of_xlate(struct mhuv3 *mhu,
+						 unsigned int channel,
+						 unsigned int doorbell)
+{
+	struct mhuv3_extension *e = mhu->ext[DBE_EXT];
+	struct mbox_controller *mbox = &mhu->mbox;
+	struct mbox_chan *chans = mbox->chans;
+
+	if (channel >= e->num_chans || doorbell >= MHUV3_FLAG_BITS) {
+		dev_err(mbox->dev, "Couldn't xlate to a valid channel (%d: %d)\n",
+			channel, doorbell);
+		return ERR_PTR(-ENODEV);
+	}
+
+	return &chans[e->base_ch_idx + channel * MHUV3_FLAG_BITS + doorbell];
+}
+
+static void mhuv3_dbe_combined_irq_setup(struct mhuv3 *mhu)
+{
+	struct mhuv3_extension *e = mhu->ext[DBE_EXT];
+	int i;
+
+	if (mhu->frame == PBX_FRAME) {
+		struct pdbcw_page __iomem *dbcw = mhu->pbx->dbcw;
+
+		for (i = 0; i < e->num_chans; i++) {
+			writel_relaxed_bitmask(0x1, &dbcw[i].int_clr, tfr_ack);
+			writel_relaxed_bitmask(0x0, &dbcw[i].int_en, tfr_ack);
+			writel_relaxed_bitmask(0x1, &dbcw[i].ctrl, comb_en);
+		}
+	} else {
+		struct mdbcw_page __iomem *dbcw = mhu->mbx->dbcw;
+
+		for (i = 0; i < e->num_chans; i++) {
+			writel_relaxed(0xFFFFFFFF, &dbcw[i].clr);
+			writel_relaxed(0xFFFFFFFF, &dbcw[i].msk_set);
+			writel_relaxed_bitmask(0x1, &dbcw[i].ctrl, comb_en);
+		}
+	}
+}
+
+static int mhuv3_dbe_channels_init(struct mhuv3 *mhu)
+{
+	struct mhuv3_extension *e = mhu->ext[DBE_EXT];
+	struct mbox_controller *mbox = &mhu->mbox;
+	struct mbox_chan *chans;
+	int i;
+
+	chans = mbox->chans + mbox->num_chans;
+	e->base_ch_idx = mbox->num_chans;
+	for (i = 0; i < e->num_chans; i++) {
+		struct mhuv3_mbox_chan_priv *priv;
+		int k;
+
+		for (k = 0; k < MHUV3_FLAG_BITS; k++) {
+			priv = devm_kmalloc(mbox->dev, sizeof(*priv), GFP_KERNEL);
+			if (!priv)
+				return -ENOMEM;
+
+			priv->ch_idx = i;
+			priv->ops = &mhuv3_doorbell_ops;
+			priv->doorbell = k;
+			chans++->con_priv = priv;
+			mbox->num_chans++;
+		}
+	}
+
+	spin_lock_init(&e->pending_lock);
+
+	return 0;
+}
+
+static bool mhuv3_dbe_doorbell_lookup(struct mhuv3 *mhu, unsigned int channel,
+				      unsigned int *db)
+{
+	struct mhuv3_extension *e = mhu->ext[DBE_EXT];
+	struct device *dev = mhu->mbox.dev;
+	u32 st;
+
+	if (mhu->frame == PBX_FRAME) {
+		u32 active_dbs, fired_dbs;
+
+		st = readl_relaxed_bitmask(&mhu->pbx->dbcw[channel].int_st,
+					   tfr_ack);
+		if (!st)
+			goto err_spurious;
+
+		active_dbs = readl_relaxed(&mhu->pbx->dbcw[channel].st);
+		scoped_guard(spinlock_irqsave, &e->pending_lock) {
+			fired_dbs = e->pending_db[channel] & ~active_dbs;
+			if (!fired_dbs)
+				goto err_spurious;
+
+			*db = __ffs(fired_dbs);
+			e->pending_db[channel] &= ~BIT(*db);
+		}
+		fired_dbs &= ~BIT(*db);
+		/* Clear TFR Ack if no more doorbells pending */
+		if (!fired_dbs)
+			writel_relaxed_bitmask(0x1,
+					       &mhu->pbx->dbcw[channel].int_clr,
+					       tfr_ack);
+	} else {
+		st = readl_relaxed(&mhu->mbx->dbcw[channel].st_msk);
+		if (!st)
+			goto err_spurious;
+
+		*db = __ffs(st);
+	}
+
+	return true;
+
+err_spurious:
+	dev_warn(dev, "Spurious IRQ on %s channel:%d\n",
+		 mhuv3_str[mhu->frame], channel);
+
+	return false;
+}
+
+static struct mbox_chan *mhuv3_dbe_chan_from_comb_irq_get(struct mhuv3 *mhu)
+{
+	struct mhuv3_extension *e = mhu->ext[DBE_EXT];
+	struct device *dev = mhu->mbox.dev;
+	int i;
+
+	for (i = 0; i < MHUV3_DBCH_CMB_INT_ST_REG_CNT; i++) {
+		unsigned int channel, db;
+		u32 cmb_st;
+
+		cmb_st = readl_relaxed(&mhu->ctrl->dbch_int_st[i]);
+		if (!cmb_st)
+			continue;
+
+		channel = i * MHUV3_FLAG_BITS + __ffs(cmb_st);
+		if (channel >= e->num_chans) {
+			dev_err(dev, "Invalid %s channel:%d\n",
+				mhuv3_str[mhu->frame], channel);
+			return ERR_PTR(-EIO);
+		}
+
+		if (!mhuv3_dbe_doorbell_lookup(mhu, channel, &db))
+			continue;
+
+		dev_dbg(dev, "Found %s ch[%d]/db[%d]\n",
+			mhuv3_str[mhu->frame], channel, db);
+
+		return &mhu->mbox.chans[channel * MHUV3_FLAG_BITS + db];
+	}
+
+	return ERR_PTR(-EIO);
+}
+
+static int mhuv3_dbe_init(struct mhuv3 *mhu)
+{
+	struct device *dev = mhu->mbox.dev;
+	struct mhuv3_extension *e;
+
+	if (!readl_relaxed_bitmask(&mhu->ctrl->feat_spt0, dbe_spt))
+		return 0;
+
+	dev_dbg(dev, "%s: Initializing DBE Extension.\n", mhuv3_str[mhu->frame]);
+
+	e = devm_kzalloc(dev, sizeof(*e), GFP_KERNEL);
+	if (!e)
+		return -ENOMEM;
+
+	e->type = DBE_EXT;
+	/* Note that, by the spec, the number of channels is (num_dbch + 1) */
+	e->num_chans =
+		readl_relaxed_bitmask(&mhu->ctrl->dbch_cfg0, num_dbch) + 1;
+	e->mbox_of_xlate = mhuv3_dbe_mbox_of_xlate;
+	e->combined_irq_setup = mhuv3_dbe_combined_irq_setup;
+	e->channels_init = mhuv3_dbe_channels_init;
+	e->chan_from_comb_irq_get = mhuv3_dbe_chan_from_comb_irq_get;
+
+	mhu->num_chans += e->num_chans * MHUV3_FLAG_BITS;
+	mhu->ext[DBE_EXT] = e;
+
+	dev_dbg(dev, "%s: found %d DBE channels.\n",
+		mhuv3_str[mhu->frame], e->num_chans);
+
+	return 0;
+}
+
+static int mhuv3_fce_init(struct mhuv3 *mhu)
+{
+	struct device *dev = mhu->mbox.dev;
+
+	if (!readl_relaxed_bitmask(&mhu->ctrl->feat_spt0, fce_spt))
+		return 0;
+
+	dev_dbg(dev, "%s: FCE Extension not supported by driver.\n",
+		mhuv3_str[mhu->frame]);
+
+	return 0;
+}
+
+static int mhuv3_fe_init(struct mhuv3 *mhu)
+{
+	struct device *dev = mhu->mbox.dev;
+
+	if (!readl_relaxed_bitmask(&mhu->ctrl->feat_spt0, fe_spt))
+		return 0;
+
+	dev_dbg(dev, "%s: FE Extension not supported by driver.\n",
+		mhuv3_str[mhu->frame]);
+
+	return 0;
+}
+
+static mhuv3_extension_initializer mhuv3_extension_init[NUM_EXT] = {
+	mhuv3_dbe_init,
+	mhuv3_fce_init,
+	mhuv3_fe_init,
+};
+
+static int mhuv3_initialize_channels(struct device *dev, struct mhuv3 *mhu)
+{
+	struct mbox_controller *mbox = &mhu->mbox;
+	int i, ret = 0;
+
+	mbox->chans = devm_kcalloc(dev, mhu->num_chans,
+				   sizeof(*mbox->chans), GFP_KERNEL);
+	if (!mbox->chans)
+		return dev_err_probe(dev, -ENOMEM,
+				     "Failed to initialize channels\n");
+
+	for (i = 0; i < NUM_EXT && !ret; i++)
+		if (mhu->ext[i])
+			ret = mhu->ext[i]->channels_init(mhu);
+
+	return ret;
+}
+
+static struct mbox_chan *mhuv3_mbox_of_xlate(struct mbox_controller *mbox,
+					     const struct of_phandle_args *pa)
+{
+	struct mhuv3 *mhu = mhu_from_mbox(mbox);
+	unsigned int type, channel, param;
+
+	if (pa->args_count != MHUV3_MBOX_CELLS)
+		return ERR_PTR(-EINVAL);
+
+	type = pa->args[MHUV3_MBOX_CELL_TYPE];
+	if (type >= NUM_EXT)
+		return ERR_PTR(-EINVAL);
+
+	channel = pa->args[MHUV3_MBOX_CELL_CHWN];
+	param = pa->args[MHUV3_MBOX_CELL_PARAM];
+
+	return mhu->ext[type]->mbox_of_xlate(mhu, channel, param);
+}
+
+static void mhu_frame_cleanup_actions(void *data)
+{
+	struct mhuv3 *mhu = data;
+
+	writel_relaxed_bitmask(0x0, &mhu->ctrl->x_ctrl, op_req);
+}
+
+static int mhuv3_frame_init(struct mhuv3 *mhu, void __iomem *regs)
+{
+	struct device *dev = mhu->mbox.dev;
+	int i;
+
+	mhu->ctrl = regs;
+	mhu->frame = readl_relaxed_bitmask(&mhu->ctrl->blk_id, id);
+	if (mhu->frame > MBX_FRAME)
+		return dev_err_probe(dev, -EINVAL,
+				     "Invalid Frame type- %d\n", mhu->frame);
+
+	mhu->major = readl_relaxed_bitmask(&mhu->ctrl->aidr, arch_major_rev);
+	mhu->minor = readl_relaxed_bitmask(&mhu->ctrl->aidr, arch_minor_rev);
+	mhu->implem = readl_relaxed_bitmask(&mhu->ctrl->iidr, implementer);
+	mhu->rev = readl_relaxed_bitmask(&mhu->ctrl->iidr, revision);
+	mhu->var = readl_relaxed_bitmask(&mhu->ctrl->iidr, variant);
+	mhu->prod_id = readl_relaxed_bitmask(&mhu->ctrl->iidr, product_id);
+	if (mhu->major != MHUV3_MAJOR_VERSION)
+		return dev_err_probe(dev, -EINVAL,
+				     "Unsupported MHU %s block - major:%d  minor:%d\n",
+				     mhuv3_str[mhu->frame], mhu->major,
+				     mhu->minor);
+
+	mhu->auto_op_full =
+		!!readl_relaxed_bitmask(&mhu->ctrl->feat_spt1, auto_op_spt);
+	/* Request the PBX/MBX to remain operational */
+	if (mhu->auto_op_full) {
+		writel_relaxed_bitmask(0x1, &mhu->ctrl->x_ctrl, op_req);
+		devm_add_action_or_reset(dev, mhu_frame_cleanup_actions, mhu);
+	}
+
+	dev_dbg(dev,
+		"Found MHU %s block - major:%d  minor:%d\n  implem:0x%X  rev:0x%X  var:0x%X  prod_id:0x%X",
+		mhuv3_str[mhu->frame], mhu->major, mhu->minor,
+		mhu->implem, mhu->rev, mhu->var, mhu->prod_id);
+
+	if (mhu->frame == PBX_FRAME)
+		mhu->pbx = regs;
+	else
+		mhu->mbx = regs;
+
+	for (i = 0; i < NUM_EXT; i++) {
+		int ret;
+
+		/*
+		 * Note that extensions initialization fails only when such
+		 * extension initialization routine fails and the extensions
+		 * was found to be supported in hardware and in software.
+		 */
+		ret = mhuv3_extension_init[i](mhu);
+		if (ret)
+			return dev_err_probe(dev, ret,
+					     "Failed to initialize %s %s\n",
+					     mhuv3_str[mhu->frame],
+					     mhuv3_ext_str[i]);
+	}
+
+	return 0;
+}
+
+static irqreturn_t mhuv3_pbx_comb_interrupt(int irq, void *arg)
+{
+	unsigned int i, found = 0;
+	struct mhuv3 *mhu = arg;
+	struct mbox_chan *chan;
+	struct device *dev;
+	int ret = IRQ_NONE;
+
+	dev = mhu->mbox.dev;
+	for (i = 0; i < NUM_EXT; i++) {
+		struct mhuv3_mbox_chan_priv *priv;
+
+		/* FCE does not participate to the PBX combined */
+		if (i == FCE_EXT || !mhu->ext[i])
+			continue;
+
+		chan = mhu->ext[i]->chan_from_comb_irq_get(mhu);
+		if (IS_ERR(chan))
+			continue;
+
+		found++;
+		priv = chan->con_priv;
+		if (!chan->cl) {
+			dev_warn(dev, "TX Ack on UNBOUND channel (%u)\n",
+				 priv->ch_idx);
+			continue;
+		}
+
+		mbox_chan_txdone(chan, 0);
+		ret = IRQ_HANDLED;
+	}
+
+	if (found == 0)
+		dev_warn_once(dev, "Failed to find channel for the TX interrupt\n");
+
+	return ret;
+}
+
+static irqreturn_t mhuv3_mbx_comb_interrupt(int irq, void *arg)
+{
+	unsigned int i, found = 0;
+	struct mhuv3 *mhu = arg;
+	struct mbox_chan *chan;
+	struct device *dev;
+	int ret = IRQ_NONE;
+
+	dev = mhu->mbox.dev;
+	for (i = 0; i < NUM_EXT; i++) {
+		struct mhuv3_mbox_chan_priv *priv;
+		void *data __free(kfree) = NULL;
+
+		if (!mhu->ext[i])
+			continue;
+
+		/* Process any extension which could be source of the IRQ */
+		chan = mhu->ext[i]->chan_from_comb_irq_get(mhu);
+		if (IS_ERR(chan))
+			continue;
+
+		found++;
+		/* From here on we need to call rx_complete even on error */
+		priv = chan->con_priv;
+		if (!chan->cl) {
+			dev_warn(dev, "RX Data on UNBOUND channel (%u)\n",
+				 priv->ch_idx);
+			goto rx_ack;
+		}
+
+		/* Read optional in-band LE data first. */
+		if (priv->ops->read_data) {
+			data = priv->ops->read_data(mhu, chan);
+			if (IS_ERR(data)) {
+				dev_err(dev,
+					"Failed to read in-band data. err:%ld\n",
+					PTR_ERR(no_free_ptr(data)));
+				goto rx_ack;
+			}
+		}
+
+		mbox_chan_received_data(chan, data);
+		ret = IRQ_HANDLED;
+
+		/*
+		 * Acknowledge transfer after any possible optional
+		 * out-of-band data has also been retrieved via
+		 * mbox_chan_received_data().
+		 */
+rx_ack:
+		if (priv->ops->rx_complete)
+			priv->ops->rx_complete(mhu, chan);
+	}
+
+	if (found == 0)
+		dev_warn_once(dev, "Failed to find channel for the RX interrupt\n");
+
+	return ret;
+}
+
+static int mhuv3_setup_pbx(struct mhuv3 *mhu)
+{
+	struct device *dev = mhu->mbox.dev;
+
+	mhu->mbox.ops = &mhuv3_sender_ops;
+
+	if (mhu->cmb_irq > 0) {
+		int ret, i;
+
+		ret = devm_request_threaded_irq(dev, mhu->cmb_irq, NULL,
+						mhuv3_pbx_comb_interrupt,
+						IRQF_ONESHOT, "mhuv3-pbx", mhu);
+		if (ret)
+			return dev_err_probe(dev, ret,
+					     "Failed to request PBX IRQ\n");
+
+		mhu->mbox.txdone_irq = true;
+		mhu->mbox.txdone_poll = false;
+
+		for (i = 0; i < NUM_EXT; i++)
+			if (mhu->ext[i])
+				mhu->ext[i]->combined_irq_setup(mhu);
+
+		dev_dbg(dev, "MHUv3 PBX IRQs initialized.\n");
+
+		return 0;
+	}
+
+	dev_info(dev, "Using PBX in Tx polling mode.\n");
+	mhu->mbox.txdone_irq = false;
+	mhu->mbox.txdone_poll = true;
+	mhu->mbox.txpoll_period = 1;
+
+	return 0;
+}
+
+static int mhuv3_setup_mbx(struct mhuv3 *mhu)
+{
+	struct device *dev = mhu->mbox.dev;
+	int ret, i;
+
+	mhu->mbox.ops = &mhuv3_receiver_ops;
+
+	if (mhu->cmb_irq <= 0)
+		return dev_err_probe(dev, -EINVAL,
+				     "MBX combined IRQ is missing !\n");
+
+	ret = devm_request_threaded_irq(dev, mhu->cmb_irq, NULL,
+					mhuv3_mbx_comb_interrupt, IRQF_ONESHOT,
+					"mhuv3-mbx", mhu);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to request MBX IRQ\n");
+
+	for (i = 0; i < NUM_EXT; i++)
+		if (mhu->ext[i])
+			mhu->ext[i]->combined_irq_setup(mhu);
+
+	dev_dbg(dev, "MHUv3 MBX IRQs initialized.\n");
+
+	return ret;
+}
+
+static int mhuv3_irqs_init(struct mhuv3 *mhu, struct platform_device *pdev)
+{
+	dev_dbg(mhu->mbox.dev, "Initializing %s block.\n",
+		mhuv3_str[mhu->frame]);
+
+	if (mhu->frame == PBX_FRAME) {
+		mhu->cmb_irq =
+			platform_get_irq_byname_optional(pdev, "combined");
+		return mhuv3_setup_pbx(mhu);
+	}
+
+	mhu->cmb_irq = platform_get_irq_byname(pdev, "combined");
+	return mhuv3_setup_mbx(mhu);
+}
+
+static int mhuv3_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	void __iomem *regs;
+	struct mhuv3 *mhu;
+	int ret;
+
+	mhu = devm_kzalloc(dev, sizeof(*mhu), GFP_KERNEL);
+	if (!mhu)
+		return -ENOMEM;
+
+	regs = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(regs))
+		return PTR_ERR(regs);
+
+	mhu->mbox.dev = dev;
+	ret = mhuv3_frame_init(mhu, regs);
+	if (ret)
+		return ret;
+
+	ret = mhuv3_irqs_init(mhu, pdev);
+	if (ret)
+		return ret;
+
+	mhu->mbox.of_xlate = mhuv3_mbox_of_xlate;
+	ret = mhuv3_initialize_channels(dev, mhu);
+	if (ret)
+		return ret;
+
+	ret = devm_mbox_controller_register(dev, &mhu->mbox);
+	if (ret)
+		return dev_err_probe(dev, ret,
+				     "Failed to register ARM MHUv3 driver\n");
+
+	return ret;
+}
+
+static const struct of_device_id mhuv3_of_match[] = {
+	{ .compatible = "arm,mhuv3", .data = NULL },
+	{}
+};
+MODULE_DEVICE_TABLE(of, mhuv3_of_match);
+
+static struct platform_driver mhuv3_driver = {
+	.driver = {
+		.name = "arm-mhuv3-mailbox",
+		.of_match_table = mhuv3_of_match,
+	},
+	.probe = mhuv3_probe,
+};
+module_platform_driver(mhuv3_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("ARM MHUv3 Driver");
+MODULE_AUTHOR("Cristian Marussi <cristian.marussi@arm.com>");
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH v5 2/2] mailbox: arm_mhuv3: Add driver
@ 2024-04-18 10:52   ` Cristian Marussi
  0 siblings, 0 replies; 6+ messages in thread
From: Cristian Marussi @ 2024-04-18 10:52 UTC (permalink / raw)
  To: linux-kernel, linux-arm-kernel, devicetree
  Cc: sudeep.holla, cristian.marussi, jassisinghbrar, robh+dt,
	krzysztof.kozlowski+dt, conor+dt

Add support for ARM MHUv3 mailbox controller.

Support is limited to the MHUv3 Doorbell extension using only the PBX/MBX
combined interrupts.

Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
---
v4 -> v5
- changed Kconfig depends on to just need oF && HAS_IOMEM
- fixed LLVM warnings on FIELD_PREP as reported-by <lkp@intel.com>

v3 -> v4
- avoid magic numbers for padding holes
- renaming various enums terminators to count_ instead of max_
- using scoped_guards for spinlock_save
- dropping FIRST_EXT naming for 0-indexed enum
- less indentation by using early returns or continue on failure-paths
- use dev_err_probe where appropriate
- less noisy with dev_dbg
- refactored mhuv3_mbx_comb_interrupt using __free cleanups for .read_data
- refactored doorbell lookups with scoped_guards
- fail on IRQ request failures: not carrying-on best effort
- drop usage of platform_set_drvdata and .remove in favour of
  devm_add_action_or_reset
- review failures handling on extensions initialization
- removed name clashes
- more comments on regs decorations
- decreasing line-lengths definitions
- use __ffs instead of __builtin_ctz
- dropped used of bitfields in favour of bitmasks
- reading implementer/revision/variant/product_id
- fixed misspellings

v1 -> v2
- fixed checkpatch warnings about side-effects
- fixed sparse errors as reported
  | Reported-by: kernel test robot <lkp@intel.com>
  | Closes: https://lore.kernel.org/oe-kbuild-all/202403290015.tCLXudqC-lkp@intel.com/
---
 MAINTAINERS                 |    9 +
 drivers/mailbox/Kconfig     |   12 +
 drivers/mailbox/Makefile    |    2 +
 drivers/mailbox/arm_mhuv3.c | 1103 +++++++++++++++++++++++++++++++++++
 4 files changed, 1126 insertions(+)
 create mode 100644 drivers/mailbox/arm_mhuv3.c

diff --git a/MAINTAINERS b/MAINTAINERS
index aa3b947fb080..e957b9d9e32a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -12998,6 +12998,15 @@ F:	Documentation/devicetree/bindings/mailbox/arm,mhuv2.yaml
 F:	drivers/mailbox/arm_mhuv2.c
 F:	include/linux/mailbox/arm_mhuv2_message.h
 
+MAILBOX ARM MHUv3
+M:	Sudeep Holla <sudeep.holla@arm.com>
+M:	Cristian Marussi <cristian.marussi@arm.com>
+L:	linux-kernel@vger.kernel.org
+L:	linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S:	Maintained
+F:	Documentation/devicetree/bindings/mailbox/arm,mhuv3.yaml
+F:	drivers/mailbox/arm_mhuv3.c
+
 MAN-PAGES: MANUAL PAGES FOR LINUX -- Sections 2, 3, 4, 5, and 7
 M:	Alejandro Colomar <alx@kernel.org>
 L:	linux-man@vger.kernel.org
diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig
index 42940108a187..cce8c33688fa 100644
--- a/drivers/mailbox/Kconfig
+++ b/drivers/mailbox/Kconfig
@@ -23,6 +23,18 @@ config ARM_MHU_V2
 	  Say Y here if you want to build the ARM MHUv2 controller driver,
 	  which provides unidirectional mailboxes between processing elements.
 
+config ARM_MHU_V3
+	tristate "ARM MHUv3 Mailbox"
+	depends on HAS_IOMEM || COMPILE_TEST
+	depends on OF
+	help
+	  Say Y here if you want to build the ARM MHUv3 controller driver,
+	  which provides unidirectional mailboxes between processing elements.
+
+	  ARM MHUv3 controllers can implement a varying number of extensions
+	  that provides different means of transports: supported extensions
+	  will be discovered and possibly managed at probe-time.
+
 config IMX_MBOX
 	tristate "i.MX Mailbox"
 	depends on ARCH_MXC || COMPILE_TEST
diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile
index 18793e6caa2f..5cf2f54debaf 100644
--- a/drivers/mailbox/Makefile
+++ b/drivers/mailbox/Makefile
@@ -9,6 +9,8 @@ obj-$(CONFIG_ARM_MHU)	+= arm_mhu.o arm_mhu_db.o
 
 obj-$(CONFIG_ARM_MHU_V2)	+= arm_mhuv2.o
 
+obj-$(CONFIG_ARM_MHU_V3)	+= arm_mhuv3.o
+
 obj-$(CONFIG_IMX_MBOX)	+= imx-mailbox.o
 
 obj-$(CONFIG_ARMADA_37XX_RWTM_MBOX)	+= armada-37xx-rwtm-mailbox.o
diff --git a/drivers/mailbox/arm_mhuv3.c b/drivers/mailbox/arm_mhuv3.c
new file mode 100644
index 000000000000..b97e79a5870f
--- /dev/null
+++ b/drivers/mailbox/arm_mhuv3.c
@@ -0,0 +1,1103 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ARM Message Handling Unit Version 3 (MHUv3) driver.
+ *
+ * Copyright (C) 2024 ARM Ltd.
+ *
+ * Based on ARM MHUv2 driver.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/bits.h>
+#include <linux/cleanup.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/mailbox_controller.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/sizes.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+/* ====== MHUv3 Registers ====== */
+
+/* Maximum number of Doorbell channel windows */
+#define MHUV3_DBCW_MAX			128
+/* Number of DBCH combined interrupt status registers */
+#define MHUV3_DBCH_CMB_INT_ST_REG_CNT	4
+
+/* Number of FFCH combined interrupt status registers */
+#define MHUV3_FFCH_CMB_INT_ST_REG_CNT	2
+
+#define MHUV3_FLAG_BITS			32
+
+/* Not a typo ... */
+#define MHUV3_MAJOR_VERSION		2
+
+enum {
+	MHUV3_MBOX_CELL_TYPE,
+	MHUV3_MBOX_CELL_CHWN,
+	MHUV3_MBOX_CELL_PARAM,
+	MHUV3_MBOX_CELLS
+};
+
+/* Padding bitfields/fields represents hole in the regs MMIO */
+
+/* CTRL_Page */
+struct blk_id {
+#define id		GENMASK(3, 0)
+	u32 val;
+} __packed;
+
+struct feat_spt0 {
+#define	dbe_spt		GENMASK(3, 0)
+#define	fe_spt		GENMASK(7, 4)
+#define	fce_spt		GENMASK(11, 8)
+	u32 val;
+} __packed;
+
+struct feat_spt1 {
+#define	auto_op_spt	GENMASK(3, 0)
+	u32 val;
+} __packed;
+
+struct dbch_cfg0 {
+#define num_dbch	GENMASK(7, 0)
+	u32 val;
+} __packed;
+
+struct ffch_cfg0 {
+#define num_ffch	GENMASK(7, 0)
+#define x8ba_spt	BIT(8)
+#define x16ba_spt	BIT(9)
+#define x32ba_spt	BIT(10)
+#define x64ba_spt	BIT(11)
+#define ffch_depth	GENMASK(25, 16)
+	u32 val;
+} __packed;
+
+struct fch_cfg0 {
+#define num_fch		GENMASK(9, 0)
+#define fcgi_spt	BIT(10)		// MBX-only
+#define num_fcg		GENMASK(15, 11)
+#define num_fch_per_grp	GENMASK(20, 16)
+#define fch_ws		GENMASK(28, 21)
+	u32 val;
+} __packed;
+
+struct ctrl {
+#define op_req		BIT(0)
+#define	ch_op_mask	BIT(1)
+	u32 val;
+} __packed;
+
+struct fch_ctrl {
+#define _int_en		BIT(2)
+	u32 val;
+} __packed;
+
+struct iidr {
+#define implementer	GENMASK(11, 0)
+#define revision	GENMASK(15, 12)
+#define variant		GENMASK(19, 16)
+#define product_id	GENMASK(31, 20)
+	u32 val;
+} __packed;
+
+struct aidr {
+#define arch_minor_rev	GENMASK(3, 0)
+#define arch_major_rev	GENMASK(7, 4)
+	u32 val;
+} __packed;
+
+struct ctrl_page {
+	struct blk_id blk_id;
+	u8 pad[12];
+	struct feat_spt0 feat_spt0;
+	struct feat_spt1 feat_spt1;
+	u8 pad1[8];
+	struct dbch_cfg0 dbch_cfg0;
+	u8 pad2[12];
+	struct ffch_cfg0 ffch_cfg0;
+	u8 pad3[12];
+	struct fch_cfg0 fch_cfg0;
+	u8 pad4[188];
+	struct ctrl x_ctrl;
+	/*-- MBX-only registers --*/
+	u8 pad5[60];
+	struct fch_ctrl fch_ctrl;
+	u32 fcg_int_en;
+	u8 pad6[696];
+	/*-- End of MBX-only ---- */
+	u32 dbch_int_st[MHUV3_DBCH_CMB_INT_ST_REG_CNT];
+	u32 ffch_int_st[MHUV3_FFCH_CMB_INT_ST_REG_CNT];
+	/*-- MBX-only registers --*/
+	u8 pad7[88];
+	u32 fcg_int_st;
+	u8 pad8[12];
+	u32 fcg_grp_int_st[32];
+	u8 pad9[2760];
+	/*-- End of MBX-only ---- */
+	struct iidr iidr;
+	struct aidr aidr;
+	u32 imp_def_id[12];
+} __packed;
+
+/* DBCW_Page */
+
+struct xbcw_ctrl {
+#define comb_en		BIT(0)
+	u32 val;
+} __packed;
+
+struct pdbcw_int {
+#define tfr_ack		BIT(0)
+	u32 val;
+} __packed;
+
+struct pdbcw_page {
+	u32 st;
+	u8 pad[8];
+	u32 set;
+	struct pdbcw_int int_st;
+	struct pdbcw_int int_clr;
+	struct pdbcw_int int_en;
+	struct xbcw_ctrl ctrl;
+} __packed;
+
+struct mdbcw_page {
+	u32 st;
+	u32 st_msk;
+	u32 clr;
+	u8 pad[4];
+	u32 msk_st;
+	u32 msk_set;
+	u32 msk_clr;
+	struct xbcw_ctrl ctrl;
+} __packed;
+
+struct dummy_page {
+	u8 pad[SZ_4K];
+} __packed;
+
+struct mhu3_pbx_frame_reg {
+	struct ctrl_page ctrl;
+	struct pdbcw_page dbcw[MHUV3_DBCW_MAX];
+	struct dummy_page ffcw;
+	struct dummy_page fcw;
+	u8 pad[SZ_4K * 11];
+	struct dummy_page impdef;
+} __packed;
+
+struct mhu3_mbx_frame_reg {
+	struct ctrl_page ctrl;
+	struct mdbcw_page dbcw[MHUV3_DBCW_MAX];
+	struct dummy_page ffcw;
+	struct dummy_page fcw;
+	u8 pad[SZ_4K * 11];
+	struct dummy_page impdef;
+} __packed;
+
+/* Macro for reading a bitmask within a physically mapped packed struct */
+#define readl_relaxed_bitmask(_regptr, _bitmask)			\
+	({								\
+		unsigned long _rval;					\
+		_rval = readl_relaxed(_regptr);				\
+		FIELD_GET(_bitmask, _rval);				\
+	})
+
+/* Macro for writing a bitmask within a physically mapped packed struct */
+#define writel_relaxed_bitmask(_value, _regptr, _bitmask)		\
+	({								\
+		unsigned long _rval;					\
+		typeof(_regptr) _rptr = _regptr;			\
+		typeof(_bitmask) _bmask = _bitmask;			\
+		_rval = readl_relaxed(_rptr);				\
+		_rval &= ~(_bmask);					\
+		_rval |= FIELD_PREP((unsigned long long)_bmask, _value);\
+		writel_relaxed(_rval, _rptr);				\
+	})
+
+/* ====== MHUv3 data structures ====== */
+
+enum mhuv3_frame {
+	PBX_FRAME,
+	MBX_FRAME,
+};
+
+static char *mhuv3_str[] = {
+	"PBX",
+	"MBX"
+};
+
+enum mhuv3_extension_type {
+	DBE_EXT,
+	FCE_EXT,
+	FE_EXT,
+	NUM_EXT
+};
+
+static char *mhuv3_ext_str[] = {
+	"DBE",
+	"FCE",
+	"FE"
+};
+
+struct mhuv3;
+
+/**
+ * struct mhuv3_protocol_ops - MHUv3 operations
+ *
+ * @rx_startup: Receiver startup callback.
+ * @rx_shutdown: Receiver shutdown callback.
+ * @read_data: Read available Sender in-band LE data (if any).
+ * @rx_complete: Acknowledge data reception to the Sender. Any out-of-band data
+ *		 has to have been already retrieved before calling this.
+ * @tx_startup: Sender startup callback.
+ * @tx_shutdown: Sender shutdown callback.
+ * @last_tx_done: Report back to the Sender if the last transfer has completed.
+ * @send_data: Send data to the receiver.
+ *
+ * Each supported transport protocol provides its own implementation of
+ * these operations.
+ */
+struct mhuv3_protocol_ops {
+	int (*rx_startup)(struct mhuv3 *mhu, struct mbox_chan *chan);
+	void (*rx_shutdown)(struct mhuv3 *mhu, struct mbox_chan *chan);
+	void *(*read_data)(struct mhuv3 *mhu, struct mbox_chan *chan);
+	void (*rx_complete)(struct mhuv3 *mhu, struct mbox_chan *chan);
+	void (*tx_startup)(struct mhuv3 *mhu, struct mbox_chan *chan);
+	void (*tx_shutdown)(struct mhuv3 *mhu, struct mbox_chan *chan);
+	int (*last_tx_done)(struct mhuv3 *mhu, struct mbox_chan *chan);
+	int (*send_data)(struct mhuv3 *mhu, struct mbox_chan *chan, void *arg);
+};
+
+/**
+ * struct mhuv3_mbox_chan_priv - MHUv3 channel private information
+ *
+ * @ch_idx: Channel window index associated to this mailbox channel.
+ * @doorbell: Doorbell bit number within the @ch_idx window.
+ *	      Only relevant to Doorbell transport.
+ * @ops: Transport protocol specific operations for this channel.
+ *
+ * Transport specific data attached to mmailbox channel priv data.
+ */
+struct mhuv3_mbox_chan_priv {
+	u32 ch_idx;
+	u32 doorbell;
+	const struct mhuv3_protocol_ops *ops;
+};
+
+/**
+ * struct mhuv3_extension - MHUv3 extension descriptor
+ *
+ * @type: Type of extension
+ * @num_chans: Max number of channels found for this extension.
+ * @base_ch_idx: First channel number assigned to this extension, picked from
+ *		 the set of all mailbox channels descriptors created.
+ * @mbox_of_xlate: Extension specific helper to parse DT and lookup associated
+ *		   channel from the related 'mboxes' property.
+ * @combined_irq_setup: Extension specific helper to setup the combined irq.
+ * @channels_init: Extension specific helper to initialize channels.
+ * @chan_from_comb_irq_get: Extension specific helper to lookup which channel
+ *			    triggered the combined irq.
+ * @pending_db: Array of per-channel pending doorbells.
+ * @pending_lock: Protect access to pending_db.
+ */
+struct mhuv3_extension {
+	enum mhuv3_extension_type type;
+	unsigned int num_chans;
+	unsigned int base_ch_idx;
+	struct mbox_chan *(*mbox_of_xlate)(struct mhuv3 *mhu,
+					   unsigned int channel,
+					   unsigned int param);
+	void (*combined_irq_setup)(struct mhuv3 *mhu);
+	int (*channels_init)(struct mhuv3 *mhu);
+	struct mbox_chan *(*chan_from_comb_irq_get)(struct mhuv3 *mhu);
+	u32 pending_db[MHUV3_DBCW_MAX];
+	/* Protect access to pending_db */
+	spinlock_t pending_lock;
+};
+
+/**
+ * struct mhuv3 - MHUv3 mailbox controller data
+ *
+ * @frame:	Frame type: MBX_FRAME or PBX_FRAME.
+ * @auto_op_full: Flag to indicate if the MHU supports AutoOp full mode.
+ * @major: MHUv3 controller architectural major version.
+ * @minor: MHUv3 controller architectural minor version.
+ * @implem: MHUv3 controller IIDR implementer.
+ * @rev: MHUv3 controller IIDR revision.
+ * @var: MHUv3 controller IIDR variant.
+ * @prod_id: MHUv3 controller IIDR product_id.
+ * @num_chans: The total number of channnels discovered across all extensions.
+ * @cmb_irq: Combined IRQ number if any found defined.
+ * @ctrl: A reference to the MHUv3 control page for this block.
+ * @pbx: Base address of the PBX register mapping region.
+ * @mbx: Base address of the MBX register mapping region.
+ * @ext: Array holding descriptors for any found implemented extension.
+ * @mbox: Mailbox controller belonging to the MHU frame.
+ */
+struct mhuv3 {
+	enum mhuv3_frame frame;
+	bool auto_op_full;
+	unsigned int major;
+	unsigned int minor;
+	unsigned int implem;
+	unsigned int rev;
+	unsigned int var;
+	unsigned int prod_id;
+	unsigned int num_chans;
+	int cmb_irq;
+	struct ctrl_page __iomem *ctrl;
+	union {
+		struct mhu3_pbx_frame_reg __iomem *pbx;
+		struct mhu3_mbx_frame_reg __iomem *mbx;
+	};
+	struct mhuv3_extension *ext[NUM_EXT];
+	struct mbox_controller mbox;
+};
+
+#define mhu_from_mbox(_mbox) container_of(_mbox, struct mhuv3, mbox)
+
+typedef int (*mhuv3_extension_initializer)(struct mhuv3 *mhu);
+
+/* =================== Doorbell transport protocol operations =============== */
+
+static void mhuv3_doorbell_tx_startup(struct mhuv3 *mhu, struct mbox_chan *chan)
+{
+	struct mhuv3_mbox_chan_priv *priv = chan->con_priv;
+
+	/* Enable Transfer Acknowledgment events */
+	writel_relaxed_bitmask(0x1, &mhu->pbx->dbcw[priv->ch_idx].int_en, tfr_ack);
+}
+
+static void mhuv3_doorbell_tx_shutdown(struct mhuv3 *mhu, struct mbox_chan *chan)
+{
+	struct mhuv3_mbox_chan_priv *priv = chan->con_priv;
+	struct mhuv3_extension *e = mhu->ext[DBE_EXT];
+	unsigned long flags;
+
+	/* Disable Channel Transfer Ack events */
+	writel_relaxed_bitmask(0x0, &mhu->pbx->dbcw[priv->ch_idx].int_en, tfr_ack);
+
+	/* Clear Channel Transfer Ack and pending doorbells */
+	writel_relaxed_bitmask(0x1, &mhu->pbx->dbcw[priv->ch_idx].int_clr, tfr_ack);
+	spin_lock_irqsave(&e->pending_lock, flags);
+	e->pending_db[priv->ch_idx] = 0;
+	spin_unlock_irqrestore(&e->pending_lock, flags);
+}
+
+static int mhuv3_doorbell_rx_startup(struct mhuv3 *mhu, struct mbox_chan *chan)
+{
+	struct mhuv3_mbox_chan_priv *priv = chan->con_priv;
+
+	/* Unmask Channel Transfer events */
+	writel_relaxed(BIT(priv->doorbell), &mhu->mbx->dbcw[priv->ch_idx].msk_clr);
+
+	return 0;
+}
+
+static void mhuv3_doorbell_rx_shutdown(struct mhuv3 *mhu,
+				       struct mbox_chan *chan)
+{
+	struct mhuv3_mbox_chan_priv *priv = chan->con_priv;
+
+	/* Mask Channel Transfer events */
+	writel_relaxed(BIT(priv->doorbell), &mhu->mbx->dbcw[priv->ch_idx].msk_set);
+}
+
+static void mhuv3_doorbell_rx_complete(struct mhuv3 *mhu, struct mbox_chan *chan)
+{
+	struct mhuv3_mbox_chan_priv *priv = chan->con_priv;
+
+	/* Clearing the pending transfer generates the Channel Transfer Ack */
+	writel_relaxed(BIT(priv->doorbell), &mhu->mbx->dbcw[priv->ch_idx].clr);
+}
+
+static int mhuv3_doorbell_last_tx_done(struct mhuv3 *mhu,
+				       struct mbox_chan *chan)
+{
+	struct mhuv3_mbox_chan_priv *priv = chan->con_priv;
+	int done;
+
+	done = !(readl_relaxed(&mhu->pbx->dbcw[priv->ch_idx].st) &
+		 BIT(priv->doorbell));
+	if (done) {
+		struct mhuv3_extension *e = mhu->ext[DBE_EXT];
+		unsigned long flags;
+
+		/* Take care to clear the pending doorbell also when polling */
+		spin_lock_irqsave(&e->pending_lock, flags);
+		e->pending_db[priv->ch_idx] &= ~BIT(priv->doorbell);
+		spin_unlock_irqrestore(&e->pending_lock, flags);
+	}
+
+	return done;
+}
+
+static int mhuv3_doorbell_send_data(struct mhuv3 *mhu, struct mbox_chan *chan,
+				    void *arg)
+{
+	struct mhuv3_mbox_chan_priv *priv = chan->con_priv;
+	struct mhuv3_extension *e = mhu->ext[DBE_EXT];
+
+	scoped_guard(spinlock_irqsave, &e->pending_lock) {
+		/* Only one in-flight Transfer is allowed per-doorbell */
+		if (e->pending_db[priv->ch_idx] & BIT(priv->doorbell))
+			return -EBUSY;
+
+		e->pending_db[priv->ch_idx] |= BIT(priv->doorbell);
+	}
+
+	writel_relaxed(BIT(priv->doorbell), &mhu->pbx->dbcw[priv->ch_idx].set);
+
+	return 0;
+}
+
+static const struct mhuv3_protocol_ops mhuv3_doorbell_ops = {
+	.tx_startup = mhuv3_doorbell_tx_startup,
+	.tx_shutdown = mhuv3_doorbell_tx_shutdown,
+	.rx_startup = mhuv3_doorbell_rx_startup,
+	.rx_shutdown = mhuv3_doorbell_rx_shutdown,
+	.rx_complete = mhuv3_doorbell_rx_complete,
+	.last_tx_done = mhuv3_doorbell_last_tx_done,
+	.send_data = mhuv3_doorbell_send_data,
+};
+
+/* Sender and receiver mailbox ops */
+static bool mhuv3_sender_last_tx_done(struct mbox_chan *chan)
+{
+	struct mhuv3_mbox_chan_priv *priv = chan->con_priv;
+	struct mhuv3 *mhu = mhu_from_mbox(chan->mbox);
+
+	return priv->ops->last_tx_done(mhu, chan);
+}
+
+static int mhuv3_sender_send_data(struct mbox_chan *chan, void *data)
+{
+	struct mhuv3_mbox_chan_priv *priv = chan->con_priv;
+	struct mhuv3 *mhu = mhu_from_mbox(chan->mbox);
+
+	if (!priv->ops->last_tx_done(mhu, chan))
+		return -EBUSY;
+
+	return priv->ops->send_data(mhu, chan, data);
+}
+
+static int mhuv3_sender_startup(struct mbox_chan *chan)
+{
+	struct mhuv3_mbox_chan_priv *priv = chan->con_priv;
+	struct mhuv3 *mhu = mhu_from_mbox(chan->mbox);
+
+	if (priv->ops->tx_startup)
+		priv->ops->tx_startup(mhu, chan);
+
+	return 0;
+}
+
+static void mhuv3_sender_shutdown(struct mbox_chan *chan)
+{
+	struct mhuv3_mbox_chan_priv *priv = chan->con_priv;
+	struct mhuv3 *mhu = mhu_from_mbox(chan->mbox);
+
+	if (priv->ops->tx_shutdown)
+		priv->ops->tx_shutdown(mhu, chan);
+}
+
+static const struct mbox_chan_ops mhuv3_sender_ops = {
+	.send_data = mhuv3_sender_send_data,
+	.startup = mhuv3_sender_startup,
+	.shutdown = mhuv3_sender_shutdown,
+	.last_tx_done = mhuv3_sender_last_tx_done,
+};
+
+static int mhuv3_receiver_startup(struct mbox_chan *chan)
+{
+	struct mhuv3_mbox_chan_priv *priv = chan->con_priv;
+	struct mhuv3 *mhu = mhu_from_mbox(chan->mbox);
+
+	return priv->ops->rx_startup(mhu, chan);
+}
+
+static void mhuv3_receiver_shutdown(struct mbox_chan *chan)
+{
+	struct mhuv3_mbox_chan_priv *priv = chan->con_priv;
+	struct mhuv3 *mhu = mhu_from_mbox(chan->mbox);
+
+	priv->ops->rx_shutdown(mhu, chan);
+}
+
+static int mhuv3_receiver_send_data(struct mbox_chan *chan, void *data)
+{
+	dev_err(chan->mbox->dev,
+		"Trying to transmit on a MBX MHUv3 frame\n");
+	return -EIO;
+}
+
+static bool mhuv3_receiver_last_tx_done(struct mbox_chan *chan)
+{
+	dev_err(chan->mbox->dev, "Trying to Tx poll on a MBX MHUv3 frame\n");
+	return true;
+}
+
+static const struct mbox_chan_ops mhuv3_receiver_ops = {
+	.send_data = mhuv3_receiver_send_data,
+	.startup = mhuv3_receiver_startup,
+	.shutdown = mhuv3_receiver_shutdown,
+	.last_tx_done = mhuv3_receiver_last_tx_done,
+};
+
+static struct mbox_chan *mhuv3_dbe_mbox_of_xlate(struct mhuv3 *mhu,
+						 unsigned int channel,
+						 unsigned int doorbell)
+{
+	struct mhuv3_extension *e = mhu->ext[DBE_EXT];
+	struct mbox_controller *mbox = &mhu->mbox;
+	struct mbox_chan *chans = mbox->chans;
+
+	if (channel >= e->num_chans || doorbell >= MHUV3_FLAG_BITS) {
+		dev_err(mbox->dev, "Couldn't xlate to a valid channel (%d: %d)\n",
+			channel, doorbell);
+		return ERR_PTR(-ENODEV);
+	}
+
+	return &chans[e->base_ch_idx + channel * MHUV3_FLAG_BITS + doorbell];
+}
+
+static void mhuv3_dbe_combined_irq_setup(struct mhuv3 *mhu)
+{
+	struct mhuv3_extension *e = mhu->ext[DBE_EXT];
+	int i;
+
+	if (mhu->frame == PBX_FRAME) {
+		struct pdbcw_page __iomem *dbcw = mhu->pbx->dbcw;
+
+		for (i = 0; i < e->num_chans; i++) {
+			writel_relaxed_bitmask(0x1, &dbcw[i].int_clr, tfr_ack);
+			writel_relaxed_bitmask(0x0, &dbcw[i].int_en, tfr_ack);
+			writel_relaxed_bitmask(0x1, &dbcw[i].ctrl, comb_en);
+		}
+	} else {
+		struct mdbcw_page __iomem *dbcw = mhu->mbx->dbcw;
+
+		for (i = 0; i < e->num_chans; i++) {
+			writel_relaxed(0xFFFFFFFF, &dbcw[i].clr);
+			writel_relaxed(0xFFFFFFFF, &dbcw[i].msk_set);
+			writel_relaxed_bitmask(0x1, &dbcw[i].ctrl, comb_en);
+		}
+	}
+}
+
+static int mhuv3_dbe_channels_init(struct mhuv3 *mhu)
+{
+	struct mhuv3_extension *e = mhu->ext[DBE_EXT];
+	struct mbox_controller *mbox = &mhu->mbox;
+	struct mbox_chan *chans;
+	int i;
+
+	chans = mbox->chans + mbox->num_chans;
+	e->base_ch_idx = mbox->num_chans;
+	for (i = 0; i < e->num_chans; i++) {
+		struct mhuv3_mbox_chan_priv *priv;
+		int k;
+
+		for (k = 0; k < MHUV3_FLAG_BITS; k++) {
+			priv = devm_kmalloc(mbox->dev, sizeof(*priv), GFP_KERNEL);
+			if (!priv)
+				return -ENOMEM;
+
+			priv->ch_idx = i;
+			priv->ops = &mhuv3_doorbell_ops;
+			priv->doorbell = k;
+			chans++->con_priv = priv;
+			mbox->num_chans++;
+		}
+	}
+
+	spin_lock_init(&e->pending_lock);
+
+	return 0;
+}
+
+static bool mhuv3_dbe_doorbell_lookup(struct mhuv3 *mhu, unsigned int channel,
+				      unsigned int *db)
+{
+	struct mhuv3_extension *e = mhu->ext[DBE_EXT];
+	struct device *dev = mhu->mbox.dev;
+	u32 st;
+
+	if (mhu->frame == PBX_FRAME) {
+		u32 active_dbs, fired_dbs;
+
+		st = readl_relaxed_bitmask(&mhu->pbx->dbcw[channel].int_st,
+					   tfr_ack);
+		if (!st)
+			goto err_spurious;
+
+		active_dbs = readl_relaxed(&mhu->pbx->dbcw[channel].st);
+		scoped_guard(spinlock_irqsave, &e->pending_lock) {
+			fired_dbs = e->pending_db[channel] & ~active_dbs;
+			if (!fired_dbs)
+				goto err_spurious;
+
+			*db = __ffs(fired_dbs);
+			e->pending_db[channel] &= ~BIT(*db);
+		}
+		fired_dbs &= ~BIT(*db);
+		/* Clear TFR Ack if no more doorbells pending */
+		if (!fired_dbs)
+			writel_relaxed_bitmask(0x1,
+					       &mhu->pbx->dbcw[channel].int_clr,
+					       tfr_ack);
+	} else {
+		st = readl_relaxed(&mhu->mbx->dbcw[channel].st_msk);
+		if (!st)
+			goto err_spurious;
+
+		*db = __ffs(st);
+	}
+
+	return true;
+
+err_spurious:
+	dev_warn(dev, "Spurious IRQ on %s channel:%d\n",
+		 mhuv3_str[mhu->frame], channel);
+
+	return false;
+}
+
+static struct mbox_chan *mhuv3_dbe_chan_from_comb_irq_get(struct mhuv3 *mhu)
+{
+	struct mhuv3_extension *e = mhu->ext[DBE_EXT];
+	struct device *dev = mhu->mbox.dev;
+	int i;
+
+	for (i = 0; i < MHUV3_DBCH_CMB_INT_ST_REG_CNT; i++) {
+		unsigned int channel, db;
+		u32 cmb_st;
+
+		cmb_st = readl_relaxed(&mhu->ctrl->dbch_int_st[i]);
+		if (!cmb_st)
+			continue;
+
+		channel = i * MHUV3_FLAG_BITS + __ffs(cmb_st);
+		if (channel >= e->num_chans) {
+			dev_err(dev, "Invalid %s channel:%d\n",
+				mhuv3_str[mhu->frame], channel);
+			return ERR_PTR(-EIO);
+		}
+
+		if (!mhuv3_dbe_doorbell_lookup(mhu, channel, &db))
+			continue;
+
+		dev_dbg(dev, "Found %s ch[%d]/db[%d]\n",
+			mhuv3_str[mhu->frame], channel, db);
+
+		return &mhu->mbox.chans[channel * MHUV3_FLAG_BITS + db];
+	}
+
+	return ERR_PTR(-EIO);
+}
+
+static int mhuv3_dbe_init(struct mhuv3 *mhu)
+{
+	struct device *dev = mhu->mbox.dev;
+	struct mhuv3_extension *e;
+
+	if (!readl_relaxed_bitmask(&mhu->ctrl->feat_spt0, dbe_spt))
+		return 0;
+
+	dev_dbg(dev, "%s: Initializing DBE Extension.\n", mhuv3_str[mhu->frame]);
+
+	e = devm_kzalloc(dev, sizeof(*e), GFP_KERNEL);
+	if (!e)
+		return -ENOMEM;
+
+	e->type = DBE_EXT;
+	/* Note that, by the spec, the number of channels is (num_dbch + 1) */
+	e->num_chans =
+		readl_relaxed_bitmask(&mhu->ctrl->dbch_cfg0, num_dbch) + 1;
+	e->mbox_of_xlate = mhuv3_dbe_mbox_of_xlate;
+	e->combined_irq_setup = mhuv3_dbe_combined_irq_setup;
+	e->channels_init = mhuv3_dbe_channels_init;
+	e->chan_from_comb_irq_get = mhuv3_dbe_chan_from_comb_irq_get;
+
+	mhu->num_chans += e->num_chans * MHUV3_FLAG_BITS;
+	mhu->ext[DBE_EXT] = e;
+
+	dev_dbg(dev, "%s: found %d DBE channels.\n",
+		mhuv3_str[mhu->frame], e->num_chans);
+
+	return 0;
+}
+
+static int mhuv3_fce_init(struct mhuv3 *mhu)
+{
+	struct device *dev = mhu->mbox.dev;
+
+	if (!readl_relaxed_bitmask(&mhu->ctrl->feat_spt0, fce_spt))
+		return 0;
+
+	dev_dbg(dev, "%s: FCE Extension not supported by driver.\n",
+		mhuv3_str[mhu->frame]);
+
+	return 0;
+}
+
+static int mhuv3_fe_init(struct mhuv3 *mhu)
+{
+	struct device *dev = mhu->mbox.dev;
+
+	if (!readl_relaxed_bitmask(&mhu->ctrl->feat_spt0, fe_spt))
+		return 0;
+
+	dev_dbg(dev, "%s: FE Extension not supported by driver.\n",
+		mhuv3_str[mhu->frame]);
+
+	return 0;
+}
+
+static mhuv3_extension_initializer mhuv3_extension_init[NUM_EXT] = {
+	mhuv3_dbe_init,
+	mhuv3_fce_init,
+	mhuv3_fe_init,
+};
+
+static int mhuv3_initialize_channels(struct device *dev, struct mhuv3 *mhu)
+{
+	struct mbox_controller *mbox = &mhu->mbox;
+	int i, ret = 0;
+
+	mbox->chans = devm_kcalloc(dev, mhu->num_chans,
+				   sizeof(*mbox->chans), GFP_KERNEL);
+	if (!mbox->chans)
+		return dev_err_probe(dev, -ENOMEM,
+				     "Failed to initialize channels\n");
+
+	for (i = 0; i < NUM_EXT && !ret; i++)
+		if (mhu->ext[i])
+			ret = mhu->ext[i]->channels_init(mhu);
+
+	return ret;
+}
+
+static struct mbox_chan *mhuv3_mbox_of_xlate(struct mbox_controller *mbox,
+					     const struct of_phandle_args *pa)
+{
+	struct mhuv3 *mhu = mhu_from_mbox(mbox);
+	unsigned int type, channel, param;
+
+	if (pa->args_count != MHUV3_MBOX_CELLS)
+		return ERR_PTR(-EINVAL);
+
+	type = pa->args[MHUV3_MBOX_CELL_TYPE];
+	if (type >= NUM_EXT)
+		return ERR_PTR(-EINVAL);
+
+	channel = pa->args[MHUV3_MBOX_CELL_CHWN];
+	param = pa->args[MHUV3_MBOX_CELL_PARAM];
+
+	return mhu->ext[type]->mbox_of_xlate(mhu, channel, param);
+}
+
+static void mhu_frame_cleanup_actions(void *data)
+{
+	struct mhuv3 *mhu = data;
+
+	writel_relaxed_bitmask(0x0, &mhu->ctrl->x_ctrl, op_req);
+}
+
+static int mhuv3_frame_init(struct mhuv3 *mhu, void __iomem *regs)
+{
+	struct device *dev = mhu->mbox.dev;
+	int i;
+
+	mhu->ctrl = regs;
+	mhu->frame = readl_relaxed_bitmask(&mhu->ctrl->blk_id, id);
+	if (mhu->frame > MBX_FRAME)
+		return dev_err_probe(dev, -EINVAL,
+				     "Invalid Frame type- %d\n", mhu->frame);
+
+	mhu->major = readl_relaxed_bitmask(&mhu->ctrl->aidr, arch_major_rev);
+	mhu->minor = readl_relaxed_bitmask(&mhu->ctrl->aidr, arch_minor_rev);
+	mhu->implem = readl_relaxed_bitmask(&mhu->ctrl->iidr, implementer);
+	mhu->rev = readl_relaxed_bitmask(&mhu->ctrl->iidr, revision);
+	mhu->var = readl_relaxed_bitmask(&mhu->ctrl->iidr, variant);
+	mhu->prod_id = readl_relaxed_bitmask(&mhu->ctrl->iidr, product_id);
+	if (mhu->major != MHUV3_MAJOR_VERSION)
+		return dev_err_probe(dev, -EINVAL,
+				     "Unsupported MHU %s block - major:%d  minor:%d\n",
+				     mhuv3_str[mhu->frame], mhu->major,
+				     mhu->minor);
+
+	mhu->auto_op_full =
+		!!readl_relaxed_bitmask(&mhu->ctrl->feat_spt1, auto_op_spt);
+	/* Request the PBX/MBX to remain operational */
+	if (mhu->auto_op_full) {
+		writel_relaxed_bitmask(0x1, &mhu->ctrl->x_ctrl, op_req);
+		devm_add_action_or_reset(dev, mhu_frame_cleanup_actions, mhu);
+	}
+
+	dev_dbg(dev,
+		"Found MHU %s block - major:%d  minor:%d\n  implem:0x%X  rev:0x%X  var:0x%X  prod_id:0x%X",
+		mhuv3_str[mhu->frame], mhu->major, mhu->minor,
+		mhu->implem, mhu->rev, mhu->var, mhu->prod_id);
+
+	if (mhu->frame == PBX_FRAME)
+		mhu->pbx = regs;
+	else
+		mhu->mbx = regs;
+
+	for (i = 0; i < NUM_EXT; i++) {
+		int ret;
+
+		/*
+		 * Note that extensions initialization fails only when such
+		 * extension initialization routine fails and the extensions
+		 * was found to be supported in hardware and in software.
+		 */
+		ret = mhuv3_extension_init[i](mhu);
+		if (ret)
+			return dev_err_probe(dev, ret,
+					     "Failed to initialize %s %s\n",
+					     mhuv3_str[mhu->frame],
+					     mhuv3_ext_str[i]);
+	}
+
+	return 0;
+}
+
+static irqreturn_t mhuv3_pbx_comb_interrupt(int irq, void *arg)
+{
+	unsigned int i, found = 0;
+	struct mhuv3 *mhu = arg;
+	struct mbox_chan *chan;
+	struct device *dev;
+	int ret = IRQ_NONE;
+
+	dev = mhu->mbox.dev;
+	for (i = 0; i < NUM_EXT; i++) {
+		struct mhuv3_mbox_chan_priv *priv;
+
+		/* FCE does not participate to the PBX combined */
+		if (i == FCE_EXT || !mhu->ext[i])
+			continue;
+
+		chan = mhu->ext[i]->chan_from_comb_irq_get(mhu);
+		if (IS_ERR(chan))
+			continue;
+
+		found++;
+		priv = chan->con_priv;
+		if (!chan->cl) {
+			dev_warn(dev, "TX Ack on UNBOUND channel (%u)\n",
+				 priv->ch_idx);
+			continue;
+		}
+
+		mbox_chan_txdone(chan, 0);
+		ret = IRQ_HANDLED;
+	}
+
+	if (found == 0)
+		dev_warn_once(dev, "Failed to find channel for the TX interrupt\n");
+
+	return ret;
+}
+
+static irqreturn_t mhuv3_mbx_comb_interrupt(int irq, void *arg)
+{
+	unsigned int i, found = 0;
+	struct mhuv3 *mhu = arg;
+	struct mbox_chan *chan;
+	struct device *dev;
+	int ret = IRQ_NONE;
+
+	dev = mhu->mbox.dev;
+	for (i = 0; i < NUM_EXT; i++) {
+		struct mhuv3_mbox_chan_priv *priv;
+		void *data __free(kfree) = NULL;
+
+		if (!mhu->ext[i])
+			continue;
+
+		/* Process any extension which could be source of the IRQ */
+		chan = mhu->ext[i]->chan_from_comb_irq_get(mhu);
+		if (IS_ERR(chan))
+			continue;
+
+		found++;
+		/* From here on we need to call rx_complete even on error */
+		priv = chan->con_priv;
+		if (!chan->cl) {
+			dev_warn(dev, "RX Data on UNBOUND channel (%u)\n",
+				 priv->ch_idx);
+			goto rx_ack;
+		}
+
+		/* Read optional in-band LE data first. */
+		if (priv->ops->read_data) {
+			data = priv->ops->read_data(mhu, chan);
+			if (IS_ERR(data)) {
+				dev_err(dev,
+					"Failed to read in-band data. err:%ld\n",
+					PTR_ERR(no_free_ptr(data)));
+				goto rx_ack;
+			}
+		}
+
+		mbox_chan_received_data(chan, data);
+		ret = IRQ_HANDLED;
+
+		/*
+		 * Acknowledge transfer after any possible optional
+		 * out-of-band data has also been retrieved via
+		 * mbox_chan_received_data().
+		 */
+rx_ack:
+		if (priv->ops->rx_complete)
+			priv->ops->rx_complete(mhu, chan);
+	}
+
+	if (found == 0)
+		dev_warn_once(dev, "Failed to find channel for the RX interrupt\n");
+
+	return ret;
+}
+
+static int mhuv3_setup_pbx(struct mhuv3 *mhu)
+{
+	struct device *dev = mhu->mbox.dev;
+
+	mhu->mbox.ops = &mhuv3_sender_ops;
+
+	if (mhu->cmb_irq > 0) {
+		int ret, i;
+
+		ret = devm_request_threaded_irq(dev, mhu->cmb_irq, NULL,
+						mhuv3_pbx_comb_interrupt,
+						IRQF_ONESHOT, "mhuv3-pbx", mhu);
+		if (ret)
+			return dev_err_probe(dev, ret,
+					     "Failed to request PBX IRQ\n");
+
+		mhu->mbox.txdone_irq = true;
+		mhu->mbox.txdone_poll = false;
+
+		for (i = 0; i < NUM_EXT; i++)
+			if (mhu->ext[i])
+				mhu->ext[i]->combined_irq_setup(mhu);
+
+		dev_dbg(dev, "MHUv3 PBX IRQs initialized.\n");
+
+		return 0;
+	}
+
+	dev_info(dev, "Using PBX in Tx polling mode.\n");
+	mhu->mbox.txdone_irq = false;
+	mhu->mbox.txdone_poll = true;
+	mhu->mbox.txpoll_period = 1;
+
+	return 0;
+}
+
+static int mhuv3_setup_mbx(struct mhuv3 *mhu)
+{
+	struct device *dev = mhu->mbox.dev;
+	int ret, i;
+
+	mhu->mbox.ops = &mhuv3_receiver_ops;
+
+	if (mhu->cmb_irq <= 0)
+		return dev_err_probe(dev, -EINVAL,
+				     "MBX combined IRQ is missing !\n");
+
+	ret = devm_request_threaded_irq(dev, mhu->cmb_irq, NULL,
+					mhuv3_mbx_comb_interrupt, IRQF_ONESHOT,
+					"mhuv3-mbx", mhu);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to request MBX IRQ\n");
+
+	for (i = 0; i < NUM_EXT; i++)
+		if (mhu->ext[i])
+			mhu->ext[i]->combined_irq_setup(mhu);
+
+	dev_dbg(dev, "MHUv3 MBX IRQs initialized.\n");
+
+	return ret;
+}
+
+static int mhuv3_irqs_init(struct mhuv3 *mhu, struct platform_device *pdev)
+{
+	dev_dbg(mhu->mbox.dev, "Initializing %s block.\n",
+		mhuv3_str[mhu->frame]);
+
+	if (mhu->frame == PBX_FRAME) {
+		mhu->cmb_irq =
+			platform_get_irq_byname_optional(pdev, "combined");
+		return mhuv3_setup_pbx(mhu);
+	}
+
+	mhu->cmb_irq = platform_get_irq_byname(pdev, "combined");
+	return mhuv3_setup_mbx(mhu);
+}
+
+static int mhuv3_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	void __iomem *regs;
+	struct mhuv3 *mhu;
+	int ret;
+
+	mhu = devm_kzalloc(dev, sizeof(*mhu), GFP_KERNEL);
+	if (!mhu)
+		return -ENOMEM;
+
+	regs = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(regs))
+		return PTR_ERR(regs);
+
+	mhu->mbox.dev = dev;
+	ret = mhuv3_frame_init(mhu, regs);
+	if (ret)
+		return ret;
+
+	ret = mhuv3_irqs_init(mhu, pdev);
+	if (ret)
+		return ret;
+
+	mhu->mbox.of_xlate = mhuv3_mbox_of_xlate;
+	ret = mhuv3_initialize_channels(dev, mhu);
+	if (ret)
+		return ret;
+
+	ret = devm_mbox_controller_register(dev, &mhu->mbox);
+	if (ret)
+		return dev_err_probe(dev, ret,
+				     "Failed to register ARM MHUv3 driver\n");
+
+	return ret;
+}
+
+static const struct of_device_id mhuv3_of_match[] = {
+	{ .compatible = "arm,mhuv3", .data = NULL },
+	{}
+};
+MODULE_DEVICE_TABLE(of, mhuv3_of_match);
+
+static struct platform_driver mhuv3_driver = {
+	.driver = {
+		.name = "arm-mhuv3-mailbox",
+		.of_match_table = mhuv3_of_match,
+	},
+	.probe = mhuv3_probe,
+};
+module_platform_driver(mhuv3_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("ARM MHUv3 Driver");
+MODULE_AUTHOR("Cristian Marussi <cristian.marussi@arm.com>");
-- 
2.34.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2024-04-18 10:52 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-04-18 10:52 [PATCH v5 0/2] Add initial ARM MHUv3 mailbox support Cristian Marussi
2024-04-18 10:52 ` Cristian Marussi
2024-04-18 10:52 ` [PATCH v5 1/2] dt-bindings: mailbox: arm,mhuv3: Add bindings Cristian Marussi
2024-04-18 10:52   ` Cristian Marussi
2024-04-18 10:52 ` [PATCH v5 2/2] mailbox: arm_mhuv3: Add driver Cristian Marussi
2024-04-18 10:52   ` Cristian Marussi

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.