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