diff --git a/Documentation/devicetree/bindings/soc/renesas/renesas,r8a78000-mfis.yaml b/Documentation/devicetree/bindings/soc/renesas/renesas,r8a78000-mfis.yaml new file mode 100644 index 000000000000..eef8c0a59e9c --- /dev/null +++ b/Documentation/devicetree/bindings/soc/renesas/renesas,r8a78000-mfis.yaml @@ -0,0 +1,187 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/soc/renesas/renesas,r8a78000-mfis.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Renesas MFIS (Multifunctional Interface) controller + +maintainers: + - Wolfram Sang + +description: + The Renesas Multifunctional Interface (MFIS) provides various functionality + like mailboxes, hardware spinlocks, product identification, error injection, + error detection and such. Parts of it can be used for communication between + different CPU cores. Those cores can be in various domains like AP, RT, or + SCP. Often multiple domain-specific MFIS instances exist in one SoC. + +properties: + compatible: + enum: + - renesas,r8a78000-mfis # R-Car X5H (AP<->AP, with PRR) + - renesas,r8a78000-mfis-scp # R-Car X5H (AP<->SCP, without PRR) + + reg: + maxItems: 2 + + reg-names: + items: + - const: common + - const: mboxes + + interrupts: + minItems: 32 + maxItems: 128 + description: + The interrupts raised by the remote doorbells. + + interrupt-names: + minItems: 32 + maxItems: 128 + description: + An interrupt name is constructed with the prefix 'ch'. Then, the + channel number as specified in the documentation of the SoC. Finally, + the letter 'i' if the interrupt is raised by the IICR register. Or 'e' + if it is raised by the EICR register. + + "#hwlock-cells": + const: 1 + + "#mbox-cells": + const: 2 + description: + The first cell is the channel number as specified in the documentation + of the SoC. The second cell may specify flags as described in the file + . + +allOf: + - if: + properties: + compatible: + contains: + const: renesas,r8a78000-mfis + then: + properties: + interrupts: + minItems: 128 + interrupt-names: + minItems: 128 + items: + pattern: "^ch[0-9]+[ie]$" + + - if: + properties: + compatible: + contains: + const: renesas,r8a78000-mfis-scp + then: + properties: + interrupts: + maxItems: 32 + interrupt-names: + maxItems: 32 + items: + pattern: "^ch[0-9]+i$" + +required: + - compatible + - reg + - reg-names + - interrupts + - interrupt-names + - "#hwlock-cells" + - "#mbox-cells" + +additionalProperties: false + +examples: + - | + #include + system-controller@189e0000 { + compatible = "renesas,r8a78000-mfis"; + reg = <0x189e0000 0x1000>, <0x18800000 0x40000>; + reg-names = "common", "mboxes"; + interrupts = , , + , , + , , + , , + , , + , , + , , + , , + , , + , , + , , + , , + , , + , , + , , + , , + , , + , , + , , + , , + , , + , , + , , + , , + , , + , , + , , + , , + , , + , , + , , + , , + , , + , , + , , + , , + , , + , , + , , + , , + , , + , , + , , + , , + , , + , , + , , + , , + , , + , , + , , + , , + , , + , , + , , + , , + , , + , , + , , + , , + , , + , , + , , + , ; + interrupt-names = "ch0i", "ch0e", "ch1i", "ch1e", "ch2i", "ch2e", "ch3i", "ch3e", + "ch4i", "ch4e", "ch5i", "ch5e", "ch6i", "ch6e", "ch7i", "ch7e", + "ch8i", "ch8e", "ch9i", "ch9e", "ch10i", "ch10e", "ch11i", "ch11e", + "ch12i", "ch12e", "ch13i", "ch13e", "ch14i", "ch14e", "ch15i", "ch15e", + "ch16i", "ch16e", "ch17i", "ch17e", "ch18i", "ch18e", "ch19i", "ch19e", + "ch20i", "ch20e", "ch21i", "ch21e", "ch22i", "ch22e", "ch23i", "ch23e", + "ch24i", "ch24e", "ch25i", "ch25e", "ch26i", "ch26e", "ch27i", "ch27e", + "ch28i", "ch28e", "ch29i", "ch29e", "ch30i", "ch30e", "ch31i", "ch31e", + "ch32i", "ch32e", "ch33i", "ch33e", "ch34i", "ch34e", "ch35i", "ch35e", + "ch36i", "ch36e", "ch37i", "ch37e", "ch38i", "ch38e", "ch39i", "ch39e", + "ch40i", "ch40e", "ch41i", "ch41e", "ch42i", "ch42e", "ch43i", "ch43e", + "ch44i", "ch44e", "ch45i", "ch45e", "ch46i", "ch46e", "ch47i", "ch47e", + "ch48i", "ch48e", "ch49i", "ch49e", "ch50i", "ch50e", "ch51i", "ch51e", + "ch52i", "ch52e", "ch53i", "ch53e", "ch54i", "ch54e", "ch55i", "ch55e", + "ch56i", "ch56e", "ch57i", "ch57e", "ch58i", "ch58e", "ch59i", "ch59e", + "ch60i", "ch60e", "ch61i", "ch61e", "ch62i", "ch62e", "ch63i", "ch63e"; + #hwlock-cells = <1>; + #mbox-cells = <2>; + }; diff --git a/drivers/soc/renesas/Kconfig b/drivers/soc/renesas/Kconfig index 26bed0fdceb0..2ab150d04bb1 100644 --- a/drivers/soc/renesas/Kconfig +++ b/drivers/soc/renesas/Kconfig @@ -465,6 +465,15 @@ config ARCH_R9A07G043 endif # RISCV +config RCAR_MFIS + tristate "Renesas R-Car MFIS driver" + depends on ARCH_RENESAS || COMPILE_TEST + depends on MAILBOX + help + Select this option to enable the Renesas R-Car MFIS core driver for + the MFIS device found on SoCs like R-Car. On families like Gen5, this + is needed to communicate with the SCP. + config PWC_RZV2M bool "Renesas RZ/V2M PWC support" if COMPILE_TEST diff --git a/drivers/soc/renesas/Makefile b/drivers/soc/renesas/Makefile index 655dbcb08747..81bde85c2178 100644 --- a/drivers/soc/renesas/Makefile +++ b/drivers/soc/renesas/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_SYS_R9A09G057) += r9a09g057-sys.o # Family obj-$(CONFIG_PWC_RZV2M) += pwc-rzv2m.o +obj-$(CONFIG_RCAR_MFIS) += rcar-mfis.o obj-$(CONFIG_RST_RCAR) += rcar-rst.o obj-$(CONFIG_RZN1_IRQMUX) += rzn1_irqmux.o obj-$(CONFIG_SYSC_RZ) += rz-sysc.o diff --git a/drivers/soc/renesas/r9a08g045-sysc.c b/drivers/soc/renesas/r9a08g045-sysc.c index 03d653d5cde5..63e4aa6a7cd0 100644 --- a/drivers/soc/renesas/r9a08g045-sysc.c +++ b/drivers/soc/renesas/r9a08g045-sysc.c @@ -37,18 +37,14 @@ static const struct rz_sysc_soc_id_init_data rzg3s_sysc_soc_id_init_data __initc .specific_id_mask = GENMASK(27, 0), }; -static bool rzg3s_regmap_readable_reg(struct device *dev, unsigned int reg) +static bool rzg3s_regmap_readable_writeable_reg(unsigned int reg) { switch (reg) { case SYS_XSPI_MAP_STAADD_CS0: case SYS_XSPI_MAP_ENDADD_CS0: case SYS_XSPI_MAP_STAADD_CS1: case SYS_XSPI_MAP_ENDADD_CS1: - case SYS_GETH0_CFG: - case SYS_GETH1_CFG: case SYS_PCIE_CFG: - case SYS_PCIE_MON: - case SYS_PCIE_ERR_MON: case SYS_PCIE_PHY: case SYS_I2C0_CFG: case SYS_I2C1_CFG: @@ -63,26 +59,25 @@ static bool rzg3s_regmap_readable_reg(struct device *dev, unsigned int reg) } } +static bool rzg3s_regmap_readable_reg(struct device *dev, unsigned int reg) +{ + if (rzg3s_regmap_readable_writeable_reg(reg)) + return true; + + switch (reg) { + case SYS_GETH0_CFG: + case SYS_GETH1_CFG: + case SYS_PCIE_MON: + case SYS_PCIE_ERR_MON: + return true; + default: + return false; + } +} + static bool rzg3s_regmap_writeable_reg(struct device *dev, unsigned int reg) { - switch (reg) { - case SYS_XSPI_MAP_STAADD_CS0: - case SYS_XSPI_MAP_ENDADD_CS0: - case SYS_XSPI_MAP_STAADD_CS1: - case SYS_XSPI_MAP_ENDADD_CS1: - case SYS_PCIE_CFG: - case SYS_PCIE_PHY: - case SYS_I2C0_CFG: - case SYS_I2C1_CFG: - case SYS_I2C2_CFG: - case SYS_I2C3_CFG: - case SYS_I3C_CFG: - case SYS_USB_PWRRDY: - case SYS_PCIE_RST_RSM_B: - return true; - default: - return false; - } + return rzg3s_regmap_readable_writeable_reg(reg); } const struct rz_sysc_init_data rzg3s_sysc_init_data __initconst = { diff --git a/drivers/soc/renesas/r9a08g046-sysc.c b/drivers/soc/renesas/r9a08g046-sysc.c index fd98df196d0a..90db9d383539 100644 --- a/drivers/soc/renesas/r9a08g046-sysc.c +++ b/drivers/soc/renesas/r9a08g046-sysc.c @@ -28,17 +28,14 @@ #define SYS_PWRRDY_N 0xd70 #define SYS_IPCONT_SEL_CLONECH 0xe2c -static bool rzg3l_regmap_readable_reg(struct device *dev, unsigned int reg) +static bool rzg3l_regmap_readable_writeable_reg(unsigned int reg) { switch (reg) { case SYS_XSPI_MAP_STAADD_CS0: case SYS_XSPI_MAP_ENDADD_CS0: case SYS_XSPI_MAP_STAADD_CS1: case SYS_XSPI_MAP_ENDADD_CS1: - case SYS_GETH0_CFG: - case SYS_GETH1_CFG: case SYS_PCIE_CFG: - case SYS_PCIE_MON: case SYS_PCIE_PHY: case SYS_I2C0_CFG: case SYS_I2C1_CFG: @@ -53,28 +50,26 @@ static bool rzg3l_regmap_readable_reg(struct device *dev, unsigned int reg) } } -static bool rzg3l_regmap_writeable_reg(struct device *dev, unsigned int reg) +static bool rzg3l_regmap_readable_reg(struct device *dev, unsigned int reg) { + if (rzg3l_regmap_readable_writeable_reg(reg)) + return true; + switch (reg) { - case SYS_XSPI_MAP_STAADD_CS0: - case SYS_XSPI_MAP_ENDADD_CS0: - case SYS_XSPI_MAP_STAADD_CS1: - case SYS_XSPI_MAP_ENDADD_CS1: - case SYS_PCIE_CFG: - case SYS_PCIE_PHY: - case SYS_I2C0_CFG: - case SYS_I2C1_CFG: - case SYS_I2C2_CFG: - case SYS_I2C3_CFG: - case SYS_I3C_CFG: - case SYS_PWRRDY_N: - case SYS_IPCONT_SEL_CLONECH: + case SYS_GETH0_CFG: + case SYS_GETH1_CFG: + case SYS_PCIE_MON: return true; default: return false; } } +static bool rzg3l_regmap_writeable_reg(struct device *dev, unsigned int reg) +{ + return rzg3l_regmap_readable_writeable_reg(reg); +} + static const struct rz_sysc_soc_id_init_data rzg3l_sysc_soc_id_init_data __initconst = { .family = "RZ/G3L", .id = 0x87d9447, diff --git a/drivers/soc/renesas/r9a09g047-sys.c b/drivers/soc/renesas/r9a09g047-sys.c index ea3ca10fcc33..b617fb0bde7b 100644 --- a/drivers/soc/renesas/r9a09g047-sys.c +++ b/drivers/soc/renesas/r9a09g047-sys.c @@ -83,11 +83,9 @@ static const struct rz_sysc_soc_id_init_data rzg3e_sys_soc_id_init_data __initco .print_id = rzg3e_sys_print_id, }; -static bool rzg3e_regmap_readable_reg(struct device *dev, unsigned int reg) +static bool rzg3e_regmap_readable_writeable_reg(unsigned int reg) { switch (reg) { - case SYS_LSI_OTPTSU1TRMVAL0: - case SYS_LSI_OTPTSU1TRMVAL1: case SYS_SPI_STAADDCS0: case SYS_SPI_ENDADDCS0: case SYS_SPI_STAADDCS1: @@ -112,31 +110,23 @@ static bool rzg3e_regmap_readable_reg(struct device *dev, unsigned int reg) } } +static bool rzg3e_regmap_readable_reg(struct device *dev, unsigned int reg) +{ + if (rzg3e_regmap_readable_writeable_reg(reg)) + return true; + + switch (reg) { + case SYS_LSI_OTPTSU1TRMVAL0: + case SYS_LSI_OTPTSU1TRMVAL1: + return true; + default: + return false; + } +} + static bool rzg3e_regmap_writeable_reg(struct device *dev, unsigned int reg) { - switch (reg) { - case SYS_SPI_STAADDCS0: - case SYS_SPI_ENDADDCS0: - case SYS_SPI_STAADDCS1: - case SYS_SPI_ENDADDCS1: - case SYS_VSP_CLK: - case SYS_GBETH0_CFG: - case SYS_GBETH1_CFG: - case SYS_PCIE_INTX_CH0: - case SYS_PCIE_MSI1_CH0: - case SYS_PCIE_MSI2_CH0: - case SYS_PCIE_MSI3_CH0: - case SYS_PCIE_MSI4_CH0: - case SYS_PCIE_MSI5_CH0: - case SYS_PCIE_PME_CH0: - case SYS_PCIE_ACK_CH0: - case SYS_PCIE_MISC_CH0: - case SYS_PCIE_MODE_CH0: - case SYS_ADC_CFG: - return true; - default: - return false; - } + return rzg3e_regmap_readable_writeable_reg(reg); } const struct rz_sysc_init_data rzg3e_sys_init_data __initconst = { diff --git a/drivers/soc/renesas/r9a09g056-sys.c b/drivers/soc/renesas/r9a09g056-sys.c index 2a8ebc209961..e6a66688423f 100644 --- a/drivers/soc/renesas/r9a09g056-sys.c +++ b/drivers/soc/renesas/r9a09g056-sys.c @@ -88,13 +88,9 @@ static const struct rz_sysc_soc_id_init_data rzv2n_sys_soc_id_init_data __initco .print_id = rzv2n_sys_print_id, }; -static bool rzv2n_regmap_readable_reg(struct device *dev, unsigned int reg) +static bool rzv2n_regmap_readable_writeable_reg(unsigned int reg) { switch (reg) { - case SYS_LSI_OTPTSU0TRMVAL0: - case SYS_LSI_OTPTSU0TRMVAL1: - case SYS_LSI_OTPTSU1TRMVAL0: - case SYS_LSI_OTPTSU1TRMVAL1: case SYS_GBETH0_CFG: case SYS_GBETH1_CFG: case SYS_PCIE_INTX_CH0: @@ -114,26 +110,25 @@ static bool rzv2n_regmap_readable_reg(struct device *dev, unsigned int reg) } } +static bool rzv2n_regmap_readable_reg(struct device *dev, unsigned int reg) +{ + if (rzv2n_regmap_readable_writeable_reg(reg)) + return true; + + switch (reg) { + case SYS_LSI_OTPTSU0TRMVAL0: + case SYS_LSI_OTPTSU0TRMVAL1: + case SYS_LSI_OTPTSU1TRMVAL0: + case SYS_LSI_OTPTSU1TRMVAL1: + return true; + default: + return false; + } +} + static bool rzv2n_regmap_writeable_reg(struct device *dev, unsigned int reg) { - switch (reg) { - case SYS_GBETH0_CFG: - case SYS_GBETH1_CFG: - case SYS_PCIE_INTX_CH0: - case SYS_PCIE_MSI1_CH0: - case SYS_PCIE_MSI2_CH0: - case SYS_PCIE_MSI3_CH0: - case SYS_PCIE_MSI4_CH0: - case SYS_PCIE_MSI5_CH0: - case SYS_PCIE_PME_CH0: - case SYS_PCIE_ACK_CH0: - case SYS_PCIE_MISC_CH0: - case SYS_PCIE_MODE_CH0: - case SYS_ADC_CFG: - return true; - default: - return false; - } + return rzv2n_regmap_readable_writeable_reg(reg); } const struct rz_sysc_init_data rzv2n_sys_init_data __initconst = { diff --git a/drivers/soc/renesas/r9a09g057-sys.c b/drivers/soc/renesas/r9a09g057-sys.c index f3e054206acb..308492c31acb 100644 --- a/drivers/soc/renesas/r9a09g057-sys.c +++ b/drivers/soc/renesas/r9a09g057-sys.c @@ -91,13 +91,9 @@ static const struct rz_sysc_soc_id_init_data rzv2h_sys_soc_id_init_data __initco .print_id = rzv2h_sys_print_id, }; -static bool rzv2h_regmap_readable_reg(struct device *dev, unsigned int reg) +static bool rzv2h_regmap_readable_writeable_reg(unsigned int reg) { switch (reg) { - case SYS_LSI_OTPTSU0TRMVAL0: - case SYS_LSI_OTPTSU0TRMVAL1: - case SYS_LSI_OTPTSU1TRMVAL0: - case SYS_LSI_OTPTSU1TRMVAL1: case SYS_GBETH0_CFG: case SYS_GBETH1_CFG: case SYS_PCIE_INTX_CH0: @@ -128,37 +124,25 @@ static bool rzv2h_regmap_readable_reg(struct device *dev, unsigned int reg) } } +static bool rzv2h_regmap_readable_reg(struct device *dev, unsigned int reg) +{ + if (rzv2h_regmap_readable_writeable_reg(reg)) + return true; + + switch (reg) { + case SYS_LSI_OTPTSU0TRMVAL0: + case SYS_LSI_OTPTSU0TRMVAL1: + case SYS_LSI_OTPTSU1TRMVAL0: + case SYS_LSI_OTPTSU1TRMVAL1: + return true; + default: + return false; + } +} + static bool rzv2h_regmap_writeable_reg(struct device *dev, unsigned int reg) { - switch (reg) { - case SYS_GBETH0_CFG: - case SYS_GBETH1_CFG: - case SYS_PCIE_INTX_CH0: - case SYS_PCIE_MSI1_CH0: - case SYS_PCIE_MSI2_CH0: - case SYS_PCIE_MSI3_CH0: - case SYS_PCIE_MSI4_CH0: - case SYS_PCIE_MSI5_CH0: - case SYS_PCIE_PME_CH0: - case SYS_PCIE_ACK_CH0: - case SYS_PCIE_MISC_CH0: - case SYS_PCIE_MODE_CH0: - case SYS_PCIE_INTX_CH1: - case SYS_PCIE_MSI1_CH1: - case SYS_PCIE_MSI2_CH1: - case SYS_PCIE_MSI3_CH1: - case SYS_PCIE_MSI4_CH1: - case SYS_PCIE_MSI5_CH1: - case SYS_PCIE_PME_CH1: - case SYS_PCIE_ACK_CH1: - case SYS_PCIE_MISC_CH1: - case SYS_PCIE_MODE_CH1: - case SYS_PCIE_MODE: - case SYS_ADC_CFG: - return true; - default: - return false; - } + return rzv2h_regmap_readable_writeable_reg(reg); } const struct rz_sysc_init_data rzv2h_sys_init_data __initconst = { diff --git a/drivers/soc/renesas/rcar-mfis.c b/drivers/soc/renesas/rcar-mfis.c new file mode 100644 index 000000000000..b334b35306ae --- /dev/null +++ b/drivers/soc/renesas/rcar-mfis.c @@ -0,0 +1,344 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Renesas R-Car MFIS (Multifunctional Interface) driver + * + * Copyright (C) Renesas Solutions Corp. + * Kuninori Morimoto + * Wolfram Sang + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MFISWPCNTR 0x0900 +#define MFISWACNTR 0x0904 + +#define MFIS_X5H_IICR(i) ((i) * 0x1000 + 0x00) +#define MFIS_X5H_EICR(i) ((i) * 0x1000 + 0x04) + +#define MFIS_UNPROTECT_KEY 0xACCE0000 + +struct mfis_priv; + +struct mfis_reg { + void __iomem *base; + resource_size_t start; + struct mfis_priv *priv; +}; + +struct mfis_info { + u32 unprotect_mask; + unsigned int mb_num_channels; + unsigned int mb_reg_comes_from_dt:1; + unsigned int mb_tx_uses_eicr:1; + unsigned int mb_channels_are_unidir:1; +}; + +struct mfis_chan_priv { + u32 reg; + int irq; +}; + +struct mfis_priv { + spinlock_t unprotect_lock; /* guards access to the unprotection reg */ + struct device *dev; + struct mfis_reg common_reg; + struct mfis_reg mbox_reg; + const struct mfis_info *info; + + /* mailbox private data */ + struct mbox_controller mbox; + struct mfis_chan_priv *chan_privs; +}; + +static u32 mfis_read(struct mfis_reg *mreg, unsigned int reg) +{ + return ioread32(mreg->base + reg); +} + +static void mfis_write(struct mfis_reg *mreg, u32 reg, u32 val) +{ + struct mfis_priv *priv = mreg->priv; + u32 unprotect_mask = priv->info->unprotect_mask; + unsigned long flags; + u32 unprotect_code; + + /* + * [Gen4] key: 0xACCE0000, mask: 0x0000FFFF + * [Gen5] key: 0xACC00000, mask: 0x000FFFFF + */ + unprotect_code = (MFIS_UNPROTECT_KEY & ~unprotect_mask) | + ((mreg->start + reg) & unprotect_mask); + + spin_lock_irqsave(&priv->unprotect_lock, flags); + iowrite32(unprotect_code, priv->common_reg.base + MFISWACNTR); + iowrite32(val, mreg->base + reg); + spin_unlock_irqrestore(&priv->unprotect_lock, flags); +} + +/******************************************************** + * Mailbox * + ********************************************************/ + +#define mfis_mb_mbox_to_priv(_m) container_of((_m), struct mfis_priv, mbox) + +static irqreturn_t mfis_mb_iicr_interrupt(int irq, void *data) +{ + struct mbox_chan *chan = data; + struct mfis_priv *priv = mfis_mb_mbox_to_priv(chan->mbox); + struct mfis_chan_priv *chan_priv = chan->con_priv; + + mbox_chan_received_data(chan, NULL); + /* Stop remote(!) doorbell */ + mfis_write(&priv->mbox_reg, chan_priv->reg, 0); + + return IRQ_HANDLED; +} + +static int mfis_mb_startup(struct mbox_chan *chan) +{ + struct mfis_chan_priv *chan_priv = chan->con_priv; + + if (!chan_priv->irq) + return 0; + + return request_irq(chan_priv->irq, mfis_mb_iicr_interrupt, 0, + dev_name(chan->mbox->dev), chan); +} + +static void mfis_mb_shutdown(struct mbox_chan *chan) +{ + struct mfis_chan_priv *chan_priv = chan->con_priv; + + if (chan_priv->irq) + free_irq(chan_priv->irq, chan); +} + +static int mfis_mb_iicr_send_data(struct mbox_chan *chan, void *data) +{ + struct mfis_priv *priv = mfis_mb_mbox_to_priv(chan->mbox); + struct mfis_chan_priv *chan_priv = chan->con_priv; + + /* Our doorbell still active? */ + if (mfis_read(&priv->mbox_reg, chan_priv->reg) & BIT(0)) + return -EBUSY; + + /* Start our doorbell */ + mfis_write(&priv->mbox_reg, chan_priv->reg, BIT(0)); + + return 0; +} + +static bool mfis_mb_iicr_last_tx_done(struct mbox_chan *chan) +{ + struct mfis_priv *priv = mfis_mb_mbox_to_priv(chan->mbox); + struct mfis_chan_priv *chan_priv = chan->con_priv; + + /* Our doorbell still active? */ + return !(mfis_read(&priv->mbox_reg, chan_priv->reg) & BIT(0)); +} + +/* For MFIS variants using the IICR/EICR register pair */ +static const struct mbox_chan_ops mfis_iicr_ops = { + .startup = mfis_mb_startup, + .shutdown = mfis_mb_shutdown, + .send_data = mfis_mb_iicr_send_data, + .last_tx_done = mfis_mb_iicr_last_tx_done, +}; + +static struct mbox_chan *mfis_mb_of_xlate(struct mbox_controller *mbox, + const struct of_phandle_args *sp) +{ + struct mfis_priv *priv = mfis_mb_mbox_to_priv(mbox); + struct mfis_chan_priv *chan_priv; + bool tx_uses_eicr, is_only_rx; + u32 chan_num, chan_flags; + struct mbox_chan *chan; + + if (sp->args_count != 2) + return ERR_PTR(-EINVAL); + + chan_num = sp->args[0]; + chan_flags = sp->args[1]; + + if (chan_num >= priv->info->mb_num_channels) + return ERR_PTR(-EINVAL); + + /* Channel layout is described in mfis_mb_probe() */ + if (priv->info->mb_channels_are_unidir) { + is_only_rx = chan_flags & MFIS_CHANNEL_RX; + chan = mbox->chans + 2 * chan_num + is_only_rx; + } else { + is_only_rx = false; + chan = mbox->chans + chan_num; + } + + if (priv->info->mb_reg_comes_from_dt) { + tx_uses_eicr = chan_flags & MFIS_CHANNEL_EICR; + if (tx_uses_eicr) + chan += mbox->num_chans / 2; + } else { + tx_uses_eicr = priv->info->mb_tx_uses_eicr; + } + + chan_priv = chan->con_priv; + chan_priv->reg = (tx_uses_eicr ^ is_only_rx) ? MFIS_X5H_EICR(chan_num) : + MFIS_X5H_IICR(chan_num); + + if (!priv->info->mb_channels_are_unidir || is_only_rx) { + char irqname[8]; + char suffix = tx_uses_eicr ? 'i' : 'e'; + + /* "ch0i" or "ch0e" */ + scnprintf(irqname, sizeof(irqname), "ch%u%c", chan_num, suffix); + + chan_priv->irq = of_irq_get_byname(mbox->dev->of_node, irqname); + if (chan_priv->irq < 0) + return ERR_PTR(chan_priv->irq); + if (chan_priv->irq == 0) + return ERR_PTR(-ENOENT); + } + + return chan; +} + +static int mfis_mb_probe(struct mfis_priv *priv) +{ + unsigned int num_chan = priv->info->mb_num_channels; + struct device *dev = priv->dev; + struct mbox_controller *mbox; + struct mbox_chan *chan; + + if (priv->info->mb_channels_are_unidir) { + /* Channel layout: Ch0-TX, Ch0-RX, Ch1-TX... */ + num_chan *= 2; + } + + if (priv->info->mb_reg_comes_from_dt) { + /* Channel layout: IICR channels, EICR channels */ + num_chan *= 2; + } + + chan = devm_kcalloc(dev, num_chan, sizeof(*chan), GFP_KERNEL); + if (!chan) + return -ENOMEM; + + priv->chan_privs = devm_kcalloc(dev, num_chan, sizeof(*priv->chan_privs), + GFP_KERNEL); + if (!priv->chan_privs) + return -ENOMEM; + + mbox = &priv->mbox; + + for (unsigned int i = 0; i < num_chan; i++) + chan[i].con_priv = &priv->chan_privs[i]; + + mbox->chans = chan; + mbox->num_chans = num_chan; + mbox->txdone_poll = true; + mbox->ops = &mfis_iicr_ops; + mbox->dev = dev; + mbox->of_xlate = mfis_mb_of_xlate; + + return devm_mbox_controller_register(dev, mbox); +} + +/******************************************************** + * Common * + ********************************************************/ +static int mfis_reg_probe(struct platform_device *pdev, struct mfis_priv *priv, + struct mfis_reg *mreg, const char *name, bool required) +{ + struct resource *res; + void __iomem *base; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); + + /* If there is no mailbox resource, registers are in the common space */ + if (!res && !required) { + *mreg = priv->common_reg; + } else { + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + mreg->base = base; + mreg->start = res->start; + mreg->priv = priv; + } + + return 0; +} + +static int mfis_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct mfis_priv *priv; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->dev = dev; + priv->info = of_device_get_match_data(dev); + if (!priv->info) + return -ENOENT; + + spin_lock_init(&priv->unprotect_lock); + + ret = mfis_reg_probe(pdev, priv, &priv->common_reg, "common", true); + if (ret) + return ret; + + ret = mfis_reg_probe(pdev, priv, &priv->mbox_reg, "mboxes", false); + if (ret) + return ret; + + return mfis_mb_probe(priv); +} + +static const struct mfis_info mfis_info_r8a78000 = { + .unprotect_mask = 0x000fffff, + .mb_num_channels = 64, + .mb_reg_comes_from_dt = true, + .mb_channels_are_unidir = true, +}; + +static const struct mfis_info mfis_info_r8a78000_scp = { + .unprotect_mask = 0x000fffff, + .mb_num_channels = 32, + .mb_tx_uses_eicr = true, + .mb_channels_are_unidir = true, +}; + +static const struct of_device_id mfis_mfd_of_match[] = { + { .compatible = "renesas,r8a78000-mfis", .data = &mfis_info_r8a78000, }, + { .compatible = "renesas,r8a78000-mfis-scp", .data = &mfis_info_r8a78000_scp, }, + {} +}; +MODULE_DEVICE_TABLE(of, mfis_mfd_of_match); + +static struct platform_driver mfis_driver = { + .driver = { + .name = "rcar-mfis", + .of_match_table = mfis_mfd_of_match, + .suppress_bind_attrs = true, + }, + .probe = mfis_probe, +}; +module_platform_driver(mfis_driver); + +MODULE_AUTHOR("Kuninori Morimoto "); +MODULE_AUTHOR("Wolfram Sang "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Renesas R-Car MFIS driver"); diff --git a/drivers/soc/renesas/renesas-soc.c b/drivers/soc/renesas/renesas-soc.c index 38ff0b823bda..dedb2a0be586 100644 --- a/drivers/soc/renesas/renesas-soc.c +++ b/drivers/soc/renesas/renesas-soc.c @@ -442,8 +442,14 @@ static const struct renesas_id id_prr __initconst = { .mask = 0xff00, }; +static const struct renesas_id id_mfis __initconst = { + .offset = 0x44, + .mask = 0xff00, +}; + static const struct of_device_id renesas_ids[] __initconst = { { .compatible = "renesas,bsid", .data = &id_bsid }, + { .compatible = "renesas,r8a78000-mfis", .data = &id_mfis }, { .compatible = "renesas,r9a07g043-sysc", .data = &id_rzg2l }, { .compatible = "renesas,r9a07g044-sysc", .data = &id_rzg2l }, { .compatible = "renesas,r9a07g054-sysc", .data = &id_rzg2l }, @@ -468,7 +474,7 @@ static int __init renesas_soc_init(void) const char *soc_id; int ret; - match = of_match_node(renesas_socs, of_root); + match = of_machine_get_match(renesas_socs); if (!match) return -ENODEV; @@ -501,7 +507,7 @@ static int __init renesas_soc_init(void) product = readl(chipid + id->offset); iounmap(chipid); - if (id == &id_prr) { + if (id == &id_prr || id == &id_mfis) { /* R-Car M3-W ES1.1 incorrectly identifies as ES2.0 */ if ((product & 0x7fff) == 0x5210) product ^= 0x11; diff --git a/include/dt-bindings/soc/renesas,r8a78000-mfis.h b/include/dt-bindings/soc/renesas,r8a78000-mfis.h new file mode 100644 index 000000000000..147a8aefc643 --- /dev/null +++ b/include/dt-bindings/soc/renesas,r8a78000-mfis.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +#ifndef _DT_BINDINGS_SOC_RENESAS_R8A78000_MFIS_H +#define _DT_BINDINGS_SOC_RENESAS_R8A78000_MFIS_H + +/* + * Constants for the second mbox-cell of the Renesas MFIS IP core. To be treated + * as bit flags which can be ORed. + */ + +/* + * MFIS HW design before r8a78001 requires a channel to be marked as either + * TX or RX. + */ +#define MFIS_CHANNEL_TX (0 << 0) +#define MFIS_CHANNEL_RX (1 << 0) + +/* + * MFIS variants before r8a78001 work with pairs of IICR and EICR registers. + * Usually, it is specified in the datasheets which of the two a specific core + * should use. Then, it does not need extra description in DT. For plain MFIS + * of r8a78000, this is selectable, though. According to the system design and + * the firmware in use, these channels need to be marked. This is not needed + * with other versions of the MFIS, not even with MFIS-SCP of r8a78000. + */ +#define MFIS_CHANNEL_IICR (0 << 1) +#define MFIS_CHANNEL_EICR (1 << 1) + +#endif /* _DT_BINDINGS_SOC_RENESAS_R8A78000_MFIS_H */