Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

irqchip: Add driver for the RPMI system MSI service group

The RPMI specification defines a system MSI service group which
allows application processors to receive MSIs upon system events
such as graceful shutdown/reboot request, CPU hotplug event, memory
hotplug event, etc.

Add an irqchip driver for the RISC-V RPMI system MSI service group
to directly receive system MSIs in Linux kernel.

Reviewed-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Anup Patel <apatel@ventanamicro.com>
Link: https://lore.kernel.org/r/20250818040920.272664-14-apatel@ventanamicro.com
Signed-off-by: Paul Walmsley <pjw@kernel.org>

authored by

Anup Patel and committed by
Paul Walmsley
aa43953e 3e6cf384

+308
+7
drivers/irqchip/Kconfig
··· 634 634 select GENERIC_MSI_IRQ 635 635 select IRQ_MSI_LIB 636 636 637 + config RISCV_RPMI_SYSMSI 638 + bool 639 + depends on MAILBOX 640 + select IRQ_DOMAIN_HIERARCHY 641 + select GENERIC_MSI_IRQ 642 + default RISCV 643 + 637 644 config SIFIVE_PLIC 638 645 bool 639 646 depends on RISCV
+1
drivers/irqchip/Makefile
··· 106 106 obj-$(CONFIG_RISCV_APLIC) += irq-riscv-aplic-main.o irq-riscv-aplic-direct.o 107 107 obj-$(CONFIG_RISCV_APLIC_MSI) += irq-riscv-aplic-msi.o 108 108 obj-$(CONFIG_RISCV_IMSIC) += irq-riscv-imsic-state.o irq-riscv-imsic-early.o irq-riscv-imsic-platform.o 109 + obj-$(CONFIG_RISCV_RPMI_SYSMSI) += irq-riscv-rpmi-sysmsi.o 109 110 obj-$(CONFIG_SIFIVE_PLIC) += irq-sifive-plic.o 110 111 obj-$(CONFIG_STARFIVE_JH8100_INTC) += irq-starfive-jh8100-intc.o 111 112 obj-$(CONFIG_ACLINT_SSWI) += irq-aclint-sswi.o
+287
drivers/irqchip/irq-riscv-rpmi-sysmsi.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* Copyright (C) 2025 Ventana Micro Systems Inc. */ 3 + 4 + #include <linux/bits.h> 5 + #include <linux/bug.h> 6 + #include <linux/device.h> 7 + #include <linux/device/devres.h> 8 + #include <linux/dev_printk.h> 9 + #include <linux/errno.h> 10 + #include <linux/irq.h> 11 + #include <linux/irqdomain.h> 12 + #include <linux/mailbox_client.h> 13 + #include <linux/mailbox/riscv-rpmi-message.h> 14 + #include <linux/module.h> 15 + #include <linux/msi.h> 16 + #include <linux/of_irq.h> 17 + #include <linux/platform_device.h> 18 + #include <linux/types.h> 19 + 20 + struct rpmi_sysmsi_get_attrs_rx { 21 + __le32 status; 22 + __le32 sys_num_msi; 23 + __le32 flag0; 24 + __le32 flag1; 25 + }; 26 + 27 + #define RPMI_SYSMSI_MSI_ATTRIBUTES_FLAG0_PREF_PRIV BIT(0) 28 + 29 + struct rpmi_sysmsi_set_msi_state_tx { 30 + __le32 sys_msi_index; 31 + __le32 sys_msi_state; 32 + }; 33 + 34 + struct rpmi_sysmsi_set_msi_state_rx { 35 + __le32 status; 36 + }; 37 + 38 + #define RPMI_SYSMSI_MSI_STATE_ENABLE BIT(0) 39 + #define RPMI_SYSMSI_MSI_STATE_PENDING BIT(1) 40 + 41 + struct rpmi_sysmsi_set_msi_target_tx { 42 + __le32 sys_msi_index; 43 + __le32 sys_msi_address_low; 44 + __le32 sys_msi_address_high; 45 + __le32 sys_msi_data; 46 + }; 47 + 48 + struct rpmi_sysmsi_set_msi_target_rx { 49 + __le32 status; 50 + }; 51 + 52 + struct rpmi_sysmsi_priv { 53 + struct device *dev; 54 + struct mbox_client client; 55 + struct mbox_chan *chan; 56 + u32 nr_irqs; 57 + u32 gsi_base; 58 + }; 59 + 60 + static int rpmi_sysmsi_get_num_msi(struct rpmi_sysmsi_priv *priv) 61 + { 62 + struct rpmi_sysmsi_get_attrs_rx rx; 63 + struct rpmi_mbox_message msg; 64 + int ret; 65 + 66 + rpmi_mbox_init_send_with_response(&msg, RPMI_SYSMSI_SRV_GET_ATTRIBUTES, 67 + NULL, 0, &rx, sizeof(rx)); 68 + ret = rpmi_mbox_send_message(priv->chan, &msg); 69 + if (ret) 70 + return ret; 71 + if (rx.status) 72 + return rpmi_to_linux_error(le32_to_cpu(rx.status)); 73 + 74 + return le32_to_cpu(rx.sys_num_msi); 75 + } 76 + 77 + static int rpmi_sysmsi_set_msi_state(struct rpmi_sysmsi_priv *priv, 78 + u32 sys_msi_index, u32 sys_msi_state) 79 + { 80 + struct rpmi_sysmsi_set_msi_state_tx tx; 81 + struct rpmi_sysmsi_set_msi_state_rx rx; 82 + struct rpmi_mbox_message msg; 83 + int ret; 84 + 85 + tx.sys_msi_index = cpu_to_le32(sys_msi_index); 86 + tx.sys_msi_state = cpu_to_le32(sys_msi_state); 87 + rpmi_mbox_init_send_with_response(&msg, RPMI_SYSMSI_SRV_SET_MSI_STATE, 88 + &tx, sizeof(tx), &rx, sizeof(rx)); 89 + ret = rpmi_mbox_send_message(priv->chan, &msg); 90 + if (ret) 91 + return ret; 92 + if (rx.status) 93 + return rpmi_to_linux_error(le32_to_cpu(rx.status)); 94 + 95 + return 0; 96 + } 97 + 98 + static int rpmi_sysmsi_set_msi_target(struct rpmi_sysmsi_priv *priv, 99 + u32 sys_msi_index, struct msi_msg *m) 100 + { 101 + struct rpmi_sysmsi_set_msi_target_tx tx; 102 + struct rpmi_sysmsi_set_msi_target_rx rx; 103 + struct rpmi_mbox_message msg; 104 + int ret; 105 + 106 + tx.sys_msi_index = cpu_to_le32(sys_msi_index); 107 + tx.sys_msi_address_low = cpu_to_le32(m->address_lo); 108 + tx.sys_msi_address_high = cpu_to_le32(m->address_hi); 109 + tx.sys_msi_data = cpu_to_le32(m->data); 110 + rpmi_mbox_init_send_with_response(&msg, RPMI_SYSMSI_SRV_SET_MSI_TARGET, 111 + &tx, sizeof(tx), &rx, sizeof(rx)); 112 + ret = rpmi_mbox_send_message(priv->chan, &msg); 113 + if (ret) 114 + return ret; 115 + if (rx.status) 116 + return rpmi_to_linux_error(le32_to_cpu(rx.status)); 117 + 118 + return 0; 119 + } 120 + 121 + static void rpmi_sysmsi_irq_mask(struct irq_data *d) 122 + { 123 + struct rpmi_sysmsi_priv *priv = irq_data_get_irq_chip_data(d); 124 + irq_hw_number_t hwirq = irqd_to_hwirq(d); 125 + int ret; 126 + 127 + ret = rpmi_sysmsi_set_msi_state(priv, hwirq, 0); 128 + if (ret) 129 + dev_warn(priv->dev, "Failed to mask hwirq %lu (error %d)\n", hwirq, ret); 130 + irq_chip_mask_parent(d); 131 + } 132 + 133 + static void rpmi_sysmsi_irq_unmask(struct irq_data *d) 134 + { 135 + struct rpmi_sysmsi_priv *priv = irq_data_get_irq_chip_data(d); 136 + irq_hw_number_t hwirq = irqd_to_hwirq(d); 137 + int ret; 138 + 139 + irq_chip_unmask_parent(d); 140 + ret = rpmi_sysmsi_set_msi_state(priv, hwirq, RPMI_SYSMSI_MSI_STATE_ENABLE); 141 + if (ret) 142 + dev_warn(priv->dev, "Failed to unmask hwirq %lu (error %d)\n", hwirq, ret); 143 + } 144 + 145 + static void rpmi_sysmsi_write_msg(struct irq_data *d, struct msi_msg *msg) 146 + { 147 + struct rpmi_sysmsi_priv *priv = irq_data_get_irq_chip_data(d); 148 + irq_hw_number_t hwirq = irqd_to_hwirq(d); 149 + int ret; 150 + 151 + /* For zeroed MSI, do nothing as of now */ 152 + if (!msg->address_hi && !msg->address_lo && !msg->data) 153 + return; 154 + 155 + ret = rpmi_sysmsi_set_msi_target(priv, hwirq, msg); 156 + if (ret) 157 + dev_warn(priv->dev, "Failed to set target for hwirq %lu (error %d)\n", hwirq, ret); 158 + } 159 + 160 + static void rpmi_sysmsi_set_desc(msi_alloc_info_t *arg, struct msi_desc *desc) 161 + { 162 + arg->desc = desc; 163 + arg->hwirq = desc->data.icookie.value; 164 + } 165 + 166 + static int rpmi_sysmsi_translate(struct irq_domain *d, struct irq_fwspec *fwspec, 167 + unsigned long *hwirq, unsigned int *type) 168 + { 169 + struct msi_domain_info *info = d->host_data; 170 + struct rpmi_sysmsi_priv *priv = info->data; 171 + 172 + if (WARN_ON(fwspec->param_count < 1)) 173 + return -EINVAL; 174 + 175 + /* For DT, gsi_base is always zero. */ 176 + *hwirq = fwspec->param[0] - priv->gsi_base; 177 + *type = IRQ_TYPE_NONE; 178 + return 0; 179 + } 180 + 181 + static const struct msi_domain_template rpmi_sysmsi_template = { 182 + .chip = { 183 + .name = "RPMI-SYSMSI", 184 + .irq_mask = rpmi_sysmsi_irq_mask, 185 + .irq_unmask = rpmi_sysmsi_irq_unmask, 186 + #ifdef CONFIG_SMP 187 + .irq_set_affinity = irq_chip_set_affinity_parent, 188 + #endif 189 + .irq_write_msi_msg = rpmi_sysmsi_write_msg, 190 + .flags = IRQCHIP_SET_TYPE_MASKED | 191 + IRQCHIP_SKIP_SET_WAKE | 192 + IRQCHIP_MASK_ON_SUSPEND, 193 + }, 194 + 195 + .ops = { 196 + .set_desc = rpmi_sysmsi_set_desc, 197 + .msi_translate = rpmi_sysmsi_translate, 198 + }, 199 + 200 + .info = { 201 + .bus_token = DOMAIN_BUS_WIRED_TO_MSI, 202 + .flags = MSI_FLAG_USE_DEV_FWNODE, 203 + .handler = handle_simple_irq, 204 + .handler_name = "simple", 205 + }, 206 + }; 207 + 208 + static int rpmi_sysmsi_probe(struct platform_device *pdev) 209 + { 210 + struct device *dev = &pdev->dev; 211 + struct rpmi_sysmsi_priv *priv; 212 + int rc; 213 + 214 + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 215 + if (!priv) 216 + return -ENOMEM; 217 + priv->dev = dev; 218 + 219 + /* Setup mailbox client */ 220 + priv->client.dev = priv->dev; 221 + priv->client.rx_callback = NULL; 222 + priv->client.tx_block = false; 223 + priv->client.knows_txdone = true; 224 + priv->client.tx_tout = 0; 225 + 226 + /* Request mailbox channel */ 227 + priv->chan = mbox_request_channel(&priv->client, 0); 228 + if (IS_ERR(priv->chan)) 229 + return PTR_ERR(priv->chan); 230 + 231 + /* Get number of system MSIs */ 232 + rc = rpmi_sysmsi_get_num_msi(priv); 233 + if (rc < 1) { 234 + mbox_free_channel(priv->chan); 235 + if (rc) 236 + return dev_err_probe(dev, rc, "Failed to get number of system MSIs\n"); 237 + else 238 + return dev_err_probe(dev, -ENODEV, "No system MSIs found\n"); 239 + } 240 + priv->nr_irqs = rc; 241 + 242 + /* 243 + * The device MSI domain for platform devices on RISC-V architecture 244 + * is only available after the MSI controller driver is probed so, 245 + * explicitly configure here. 246 + */ 247 + if (!dev_get_msi_domain(dev)) { 248 + /* 249 + * The device MSI domain for OF devices is only set at the 250 + * time of populating/creating OF device. If the device MSI 251 + * domain is discovered later after the OF device is created 252 + * then we need to set it explicitly before using any platform 253 + * MSI functions. 254 + */ 255 + if (dev_of_node(dev)) 256 + of_msi_configure(dev, dev_of_node(dev)); 257 + 258 + if (!dev_get_msi_domain(dev)) { 259 + mbox_free_channel(priv->chan); 260 + return -EPROBE_DEFER; 261 + } 262 + } 263 + 264 + if (!msi_create_device_irq_domain(dev, MSI_DEFAULT_DOMAIN, 265 + &rpmi_sysmsi_template, 266 + priv->nr_irqs, priv, priv)) { 267 + mbox_free_channel(priv->chan); 268 + return dev_err_probe(dev, -ENOMEM, "failed to create MSI irq domain\n"); 269 + } 270 + 271 + dev_info(dev, "%u system MSIs registered\n", priv->nr_irqs); 272 + return 0; 273 + } 274 + 275 + static const struct of_device_id rpmi_sysmsi_match[] = { 276 + { .compatible = "riscv,rpmi-system-msi" }, 277 + {} 278 + }; 279 + 280 + static struct platform_driver rpmi_sysmsi_driver = { 281 + .driver = { 282 + .name = "rpmi-sysmsi", 283 + .of_match_table = rpmi_sysmsi_match, 284 + }, 285 + .probe = rpmi_sysmsi_probe, 286 + }; 287 + builtin_platform_driver(rpmi_sysmsi_driver);
+13
include/linux/mailbox/riscv-rpmi-message.h
··· 91 91 } 92 92 93 93 /* RPMI service group IDs */ 94 + #define RPMI_SRVGRP_SYSTEM_MSI 0x00002 94 95 #define RPMI_SRVGRP_CLOCK 0x00008 95 96 96 97 /* RPMI clock service IDs */ ··· 105 104 RPMI_CLK_SRV_SET_RATE = 0x07, 106 105 RPMI_CLK_SRV_GET_RATE = 0x08, 107 106 RPMI_CLK_SRV_ID_MAX_COUNT 107 + }; 108 + 109 + /* RPMI system MSI service IDs */ 110 + enum rpmi_sysmsi_service_id { 111 + RPMI_SYSMSI_SRV_ENABLE_NOTIFICATION = 0x01, 112 + RPMI_SYSMSI_SRV_GET_ATTRIBUTES = 0x02, 113 + RPMI_SYSMSI_SRV_GET_MSI_ATTRIBUTES = 0x03, 114 + RPMI_SYSMSI_SRV_SET_MSI_STATE = 0x04, 115 + RPMI_SYSMSI_SRV_GET_MSI_STATE = 0x05, 116 + RPMI_SYSMSI_SRV_SET_MSI_TARGET = 0x06, 117 + RPMI_SYSMSI_SRV_GET_MSI_TARGET = 0x07, 118 + RPMI_SYSMSI_SRV_ID_MAX_COUNT 108 119 }; 109 120 110 121 /* RPMI Linux mailbox attribute IDs */