| /* |
| * Copyright (C) 2016 Freescale Semiconductor, Inc. |
| * Copyright 2017 NXP |
| * |
| * SPDX-License-Identifier: GPL-2.0+ |
| */ |
| |
| #include <linux/delay.h> |
| #include <linux/err.h> |
| #include <linux/io.h> |
| #include <linux/mx8_mu.h> |
| #include <linux/of.h> |
| |
| static int version; |
| |
| /*! |
| * This function sets the Flag n of the MU. |
| */ |
| int32_t MU_SetFn(void __iomem *base, uint32_t Fn) |
| { |
| uint32_t reg, offset; |
| |
| reg = Fn & (~MU_CR_Fn_MASK1); |
| if (reg > 0) |
| return -EINVAL; |
| |
| offset = unlikely(version == MU_VER_ID_V10) |
| ? MU_V10_ACR_OFFSET1 : MU_ACR_OFFSET1; |
| |
| reg = readl_relaxed(base + offset); |
| /* Clear ABFn. */ |
| reg &= ~MU_CR_Fn_MASK1; |
| reg |= Fn; |
| writel_relaxed(reg, base + offset); |
| |
| return 0; |
| } |
| |
| /*! |
| * This function reads the status from status register. |
| */ |
| uint32_t MU_ReadStatus(void __iomem *base) |
| { |
| uint32_t reg, offset; |
| |
| offset = unlikely(version == MU_VER_ID_V10) |
| ? MU_V10_ASR_OFFSET1 : MU_ASR_OFFSET1; |
| |
| reg = readl_relaxed(base + offset); |
| |
| return reg; |
| } |
| |
| /*! |
| * This function enables specific RX full interrupt. |
| */ |
| void MU_EnableRxFullInt(void __iomem *base, uint32_t index) |
| { |
| uint32_t reg, offset; |
| |
| offset = unlikely(version == MU_VER_ID_V10) |
| ? MU_V10_ACR_OFFSET1 : MU_ACR_OFFSET1; |
| |
| reg = readl_relaxed(base + offset); |
| reg &= ~(MU_CR_GIRn_MASK1 | MU_CR_NMI_MASK1); |
| reg |= MU_CR_RIE0_MASK1 >> index; |
| writel_relaxed(reg, base + offset); |
| } |
| |
| /*! |
| * This function enables specific general purpose interrupt. |
| */ |
| void MU_EnableGeneralInt(void __iomem *base, uint32_t index) |
| { |
| uint32_t reg, offset; |
| |
| offset = unlikely(version == MU_VER_ID_V10) |
| ? MU_V10_ACR_OFFSET1 : MU_ACR_OFFSET1; |
| |
| reg = readl_relaxed(base + offset); |
| reg &= ~(MU_CR_GIRn_MASK1 | MU_CR_NMI_MASK1); |
| reg |= MU_CR_GIE0_MASK1 >> index; |
| writel_relaxed(reg, base + offset); |
| } |
| |
| /* |
| * Wait and send message to the other core. |
| */ |
| void MU_SendMessage(void __iomem *base, uint32_t regIndex, uint32_t msg) |
| { |
| uint32_t mask = MU_SR_TE0_MASK1 >> regIndex; |
| |
| if (unlikely(version == MU_VER_ID_V10)) { |
| /* Wait TX register to be empty. */ |
| while (!(readl_relaxed(base + MU_V10_ASR_OFFSET1) & mask)) |
| ; |
| writel_relaxed(msg, base + MU_V10_ATR0_OFFSET1 |
| + (regIndex * 4)); |
| } else { |
| /* Wait TX register to be empty. */ |
| while (!(readl_relaxed(base + MU_ASR_OFFSET1) & mask)) |
| ; |
| writel_relaxed(msg, base + MU_ATR0_OFFSET1 + (regIndex * 4)); |
| } |
| } |
| |
| /* |
| * Wait and send message to the other core with timeout mechanism. |
| */ |
| void MU_SendMessageTimeout(void __iomem *base, uint32_t regIndex, uint32_t msg, |
| uint32_t t) |
| { |
| uint32_t mask = MU_SR_TE0_MASK1 >> regIndex; |
| uint32_t timeout = t; |
| |
| if (unlikely(version == MU_VER_ID_V10)) { |
| /* Wait TX register to be empty. */ |
| while (!(readl_relaxed(base + MU_V10_ASR_OFFSET1) & mask)) { |
| udelay(10); |
| if (timeout-- == 0) |
| return; |
| }; |
| |
| writel_relaxed(msg, base + MU_V10_ATR0_OFFSET1 |
| + (regIndex * 4)); |
| } else { |
| /* Wait TX register to be empty. */ |
| while (!(readl_relaxed(base + MU_ASR_OFFSET1) & mask)) { |
| udelay(10); |
| if (timeout-- == 0) |
| return; |
| }; |
| |
| writel_relaxed(msg, base + MU_ATR0_OFFSET1 + (regIndex * 4)); |
| } |
| } |
| |
| /* |
| * Wait to receive message from the other core. |
| */ |
| void MU_ReceiveMsg(void __iomem *base, uint32_t regIndex, uint32_t *msg) |
| { |
| uint32_t mask = MU_SR_RF0_MASK1 >> regIndex; |
| |
| if (unlikely(version == MU_VER_ID_V10)) { |
| /* Wait RX register to be full. */ |
| while (!(readl_relaxed(base + MU_V10_ASR_OFFSET1) & mask)) |
| ; |
| *msg = readl_relaxed(base + MU_V10_ARR0_OFFSET1 |
| + (regIndex * 4)); |
| } else { |
| /* Wait RX register to be full. */ |
| while (!(readl_relaxed(base + MU_ASR_OFFSET1) & mask)) |
| ; |
| *msg = readl_relaxed(base + MU_ARR0_OFFSET1 + (regIndex * 4)); |
| } |
| } |
| |
| |
| |
| void MU_Init(void __iomem *base) |
| { |
| uint32_t reg, offset; |
| |
| version = readl_relaxed(base) >> 16; |
| |
| offset = unlikely(version == MU_VER_ID_V10) |
| ? MU_V10_ACR_OFFSET1 : MU_ACR_OFFSET1; |
| |
| reg = readl_relaxed(base + offset); |
| /* Clear GIEn, TIEn, GIRn and ABFn. */ |
| reg &= ~(MU_CR_GIEn_MASK1 | MU_CR_TIEn_MASK1 |
| | MU_CR_GIRn_MASK1 | MU_CR_NMI_MASK1 | MU_CR_Fn_MASK1); |
| |
| /* |
| * i.MX6SX and i.MX7D have multi-core power management which need |
| * to use RIE interrupts. |
| */ |
| if (!(of_machine_is_compatible("fsl,imx6sx") || |
| of_machine_is_compatible("fsl,imx7d"))) |
| reg &= ~MU_CR_RIEn_MASK1; |
| |
| writel_relaxed(reg, base + offset); |
| } |
| |
| /**@}*/ |
| |