| /* |
| * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. |
| * |
| * SPDX-License-Identifier: BSD-3-Clause |
| */ |
| |
| /* |
| * Zynq UltraScale+ MPSoC IPI agent registers access management |
| */ |
| |
| #include <bakery_lock.h> |
| #include <debug.h> |
| #include <errno.h> |
| #include <mmio.h> |
| #include <runtime_svc.h> |
| #include <string.h> |
| #include "zynqmp_ipi.h" |
| #include "../zynqmp_private.h" |
| |
| /********************************************************************* |
| * Macros definitions |
| ********************************************************************/ |
| |
| /* IPI registers base address */ |
| #define IPI_REGS_BASE 0xFF300000U |
| |
| /* IPI registers offsets macros */ |
| #define IPI_TRIG_OFFSET 0x00U |
| #define IPI_OBR_OFFSET 0x04U |
| #define IPI_ISR_OFFSET 0x10U |
| #define IPI_IMR_OFFSET 0x14U |
| #define IPI_IER_OFFSET 0x18U |
| #define IPI_IDR_OFFSET 0x1CU |
| |
| /* IPI register start offset */ |
| #define IPI_REG_BASE(I) (zynqmp_ipi_table[(I)].ipi_reg_base) |
| |
| /* IPI register bit mask */ |
| #define IPI_BIT_MASK(I) (zynqmp_ipi_table[(I)].ipi_bit_mask) |
| |
| /* IPI secure check */ |
| #define IPI_SECURE_MASK 0x1U |
| #define IPI_IS_SECURE(I) ((zynqmp_ipi_table[(I)].secure_only & \ |
| IPI_SECURE_MASK) ? 1 : 0) |
| |
| /********************************************************************* |
| * Struct definitions |
| ********************************************************************/ |
| |
| /* structure to maintain IPI configuration information */ |
| struct zynqmp_ipi_config { |
| unsigned int ipi_bit_mask; |
| unsigned int ipi_reg_base; |
| unsigned char secure_only; |
| }; |
| |
| /* Zynqmp ipi configuration table */ |
| const static struct zynqmp_ipi_config zynqmp_ipi_table[] = { |
| /* APU IPI */ |
| { |
| .ipi_bit_mask = 0x1, |
| .ipi_reg_base = 0xFF300000, |
| .secure_only = 0, |
| }, |
| /* RPU0 IPI */ |
| { |
| .ipi_bit_mask = 0x100, |
| .ipi_reg_base = 0xFF310000, |
| .secure_only = 0, |
| }, |
| /* RPU1 IPI */ |
| { |
| .ipi_bit_mask = 0x200, |
| .ipi_reg_base = 0xFF320000, |
| .secure_only = 0, |
| }, |
| /* PMU0 IPI */ |
| { |
| .ipi_bit_mask = 0x10000, |
| .ipi_reg_base = 0xFF330000, |
| .secure_only = IPI_SECURE_MASK, |
| }, |
| /* PMU1 IPI */ |
| { |
| .ipi_bit_mask = 0x20000, |
| .ipi_reg_base = 0xFF331000, |
| .secure_only = IPI_SECURE_MASK, |
| }, |
| /* PMU2 IPI */ |
| { |
| .ipi_bit_mask = 0x40000, |
| .ipi_reg_base = 0xFF332000, |
| .secure_only = IPI_SECURE_MASK, |
| }, |
| /* PMU3 IPI */ |
| { |
| .ipi_bit_mask = 0x80000, |
| .ipi_reg_base = 0xFF333000, |
| .secure_only = IPI_SECURE_MASK, |
| }, |
| /* PL0 IPI */ |
| { |
| .ipi_bit_mask = 0x1000000, |
| .ipi_reg_base = 0xFF340000, |
| .secure_only = 0, |
| }, |
| /* PL1 IPI */ |
| { |
| .ipi_bit_mask = 0x2000000, |
| .ipi_reg_base = 0xFF350000, |
| .secure_only = 0, |
| }, |
| /* PL2 IPI */ |
| { |
| .ipi_bit_mask = 0x4000000, |
| .ipi_reg_base = 0xFF360000, |
| .secure_only = 0, |
| }, |
| /* PL3 IPI */ |
| { |
| .ipi_bit_mask = 0x8000000, |
| .ipi_reg_base = 0xFF370000, |
| .secure_only = 0, |
| }, |
| }; |
| |
| /* is_ipi_mb_within_range() - verify if IPI mailbox is within range |
| * |
| * @local - local IPI ID |
| * @remote - remote IPI ID |
| * |
| * return - 1 if within range, 0 if not |
| */ |
| static inline int is_ipi_mb_within_range(uint32_t local, uint32_t remote) |
| { |
| int ret = 1; |
| uint32_t ipi_total = ARRAY_SIZE(zynqmp_ipi_table); |
| |
| if (remote >= ipi_total || local >= ipi_total) |
| ret = 0; |
| |
| return ret; |
| } |
| |
| /** |
| * ipi_mb_validate() - validate IPI mailbox access |
| * |
| * @local - local IPI ID |
| * @remote - remote IPI ID |
| * @is_secure - indicate if the requester is from secure software |
| * |
| * return - 0 success, negative value for errors |
| */ |
| int ipi_mb_validate(uint32_t local, uint32_t remote, unsigned int is_secure) |
| { |
| int ret = 0; |
| |
| if (!is_ipi_mb_within_range(local, remote)) |
| ret = -EINVAL; |
| else if (IPI_IS_SECURE(local) && !is_secure) |
| ret = -EPERM; |
| else if (IPI_IS_SECURE(remote) && !is_secure) |
| ret = -EPERM; |
| |
| return ret; |
| } |
| |
| /** |
| * ipi_mb_open() - Open IPI mailbox. |
| * |
| * @local - local IPI ID |
| * @remote - remote IPI ID |
| * |
| */ |
| void ipi_mb_open(uint32_t local, uint32_t remote) |
| { |
| mmio_write_32(IPI_REG_BASE(local) + IPI_IDR_OFFSET, |
| IPI_BIT_MASK(remote)); |
| mmio_write_32(IPI_REG_BASE(local) + IPI_ISR_OFFSET, |
| IPI_BIT_MASK(remote)); |
| } |
| |
| /** |
| * ipi_mb_release() - Open IPI mailbox. |
| * |
| * @local - local IPI ID |
| * @remote - remote IPI ID |
| * |
| */ |
| void ipi_mb_release(uint32_t local, uint32_t remote) |
| { |
| mmio_write_32(IPI_REG_BASE(local) + IPI_IDR_OFFSET, |
| IPI_BIT_MASK(remote)); |
| } |
| |
| /** |
| * ipi_mb_enquire_status() - Enquire IPI mailbox status |
| * |
| * @local - local IPI ID |
| * @remote - remote IPI ID |
| * |
| * return - 0 idle, positive value for pending sending or receiving, |
| * negative value for errors |
| */ |
| int ipi_mb_enquire_status(uint32_t local, uint32_t remote) |
| { |
| int ret = 0; |
| uint32_t status; |
| |
| status = mmio_read_32(IPI_REG_BASE(local) + IPI_OBR_OFFSET); |
| if (status & IPI_BIT_MASK(remote)) |
| ret |= IPI_MB_STATUS_SEND_PENDING; |
| status = mmio_read_32(IPI_REG_BASE(local) + IPI_ISR_OFFSET); |
| if (status & IPI_BIT_MASK(remote)) |
| ret |= IPI_MB_STATUS_RECV_PENDING; |
| |
| return ret; |
| } |
| |
| /* ipi_mb_notify() - Trigger IPI mailbox notification |
| * |
| * @local - local IPI ID |
| * @remote - remote IPI ID |
| * @is_blocking - if to trigger the notification in blocking mode or not. |
| * |
| * It sets the remote bit in the IPI agent trigger register. |
| * |
| */ |
| void ipi_mb_notify(uint32_t local, uint32_t remote, uint32_t is_blocking) |
| { |
| uint32_t status; |
| |
| mmio_write_32(IPI_REG_BASE(local) + IPI_TRIG_OFFSET, |
| IPI_BIT_MASK(remote)); |
| if (is_blocking) { |
| do { |
| status = mmio_read_32(IPI_REG_BASE(local) + |
| IPI_OBR_OFFSET); |
| } while (status & IPI_BIT_MASK(remote)); |
| } |
| } |
| |
| /* ipi_mb_ack() - Ack IPI mailbox notification from the other end |
| * |
| * @local - local IPI ID |
| * @remote - remote IPI ID |
| * |
| * It will clear the remote bit in the isr register. |
| * |
| */ |
| void ipi_mb_ack(uint32_t local, uint32_t remote) |
| { |
| mmio_write_32(IPI_REG_BASE(local) + IPI_ISR_OFFSET, |
| IPI_BIT_MASK(remote)); |
| } |
| |
| /* ipi_mb_disable_irq() - Disable IPI mailbox notification interrupt |
| * |
| * @local - local IPI ID |
| * @remote - remote IPI ID |
| * |
| * It will mask the remote bit in the idr register. |
| * |
| */ |
| void ipi_mb_disable_irq(uint32_t local, uint32_t remote) |
| { |
| mmio_write_32(IPI_REG_BASE(local) + IPI_IDR_OFFSET, |
| IPI_BIT_MASK(remote)); |
| } |
| |
| /* ipi_mb_enable_irq() - Enable IPI mailbox notification interrupt |
| * |
| * @local - local IPI ID |
| * @remote - remote IPI ID |
| * |
| * It will mask the remote bit in the idr register. |
| * |
| */ |
| void ipi_mb_enable_irq(uint32_t local, uint32_t remote) |
| { |
| mmio_write_32(IPI_REG_BASE(local) + IPI_IER_OFFSET, |
| IPI_BIT_MASK(remote)); |
| } |