1// SPDX-License-Identifier: GPL-2.0 2/* 3 * AMD CDX bus driver MSI support 4 * 5 * Copyright (C) 2022-2023, Advanced Micro Devices, Inc. 6 */ 7 8#include <linux/of.h> 9#include <linux/of_device.h> 10#include <linux/of_address.h> 11#include <linux/of_irq.h> 12#include <linux/irq.h> 13#include <linux/irqdomain.h> 14#include <linux/msi.h> 15#include <linux/cdx/cdx_bus.h> 16 17#include "cdx.h" 18 19static void cdx_msi_write_msg(struct irq_data *irq_data, struct msi_msg *msg) 20{ 21 struct msi_desc *msi_desc = irq_data_get_msi_desc(irq_data); 22 struct cdx_device *cdx_dev = to_cdx_device(msi_desc->dev); 23 24 /* We would not operate on msg here rather we wait for irq_bus_sync_unlock() 25 * to be called from preemptible task context. 26 */ 27 msi_desc->msg = *msg; 28 cdx_dev->msi_write_pending = true; 29} 30 31static void cdx_msi_write_irq_lock(struct irq_data *irq_data) 32{ 33 struct msi_desc *msi_desc = irq_data_get_msi_desc(irq_data); 34 struct cdx_device *cdx_dev = to_cdx_device(msi_desc->dev); 35 36 mutex_lock(&cdx_dev->irqchip_lock); 37} 38 39static void cdx_msi_write_irq_unlock(struct irq_data *irq_data) 40{ 41 struct msi_desc *msi_desc = irq_data_get_msi_desc(irq_data); 42 struct cdx_device *cdx_dev = to_cdx_device(msi_desc->dev); 43 struct cdx_controller *cdx = cdx_dev->cdx; 44 struct cdx_device_config dev_config; 45 46 if (!cdx_dev->msi_write_pending) { 47 mutex_unlock(&cdx_dev->irqchip_lock); 48 return; 49 } 50 51 cdx_dev->msi_write_pending = false; 52 mutex_unlock(&cdx_dev->irqchip_lock); 53 54 dev_config.msi.msi_index = msi_desc->msi_index; 55 dev_config.msi.data = msi_desc->msg.data; 56 dev_config.msi.addr = ((u64)(msi_desc->msg.address_hi) << 32) | msi_desc->msg.address_lo; 57 58 /* 59 * dev_configure() is a controller callback which can interact with 60 * Firmware or other entities, and can sleep, so invoke this function 61 * outside of the mutex held region. 62 */ 63 dev_config.type = CDX_DEV_MSI_CONF; 64 if (cdx->ops->dev_configure) 65 cdx->ops->dev_configure(cdx, cdx_dev->bus_num, cdx_dev->dev_num, &dev_config); 66} 67 68int cdx_enable_msi(struct cdx_device *cdx_dev) 69{ 70 struct cdx_controller *cdx = cdx_dev->cdx; 71 struct cdx_device_config dev_config; 72 73 dev_config.type = CDX_DEV_MSI_ENABLE; 74 dev_config.msi_enable = true; 75 if (cdx->ops->dev_configure) { 76 return cdx->ops->dev_configure(cdx, cdx_dev->bus_num, cdx_dev->dev_num, 77 &dev_config); 78 } 79 80 return -EOPNOTSUPP; 81} 82EXPORT_SYMBOL_GPL(cdx_enable_msi); 83 84void cdx_disable_msi(struct cdx_device *cdx_dev) 85{ 86 struct cdx_controller *cdx = cdx_dev->cdx; 87 struct cdx_device_config dev_config; 88 89 dev_config.type = CDX_DEV_MSI_ENABLE; 90 dev_config.msi_enable = false; 91 if (cdx->ops->dev_configure) 92 cdx->ops->dev_configure(cdx, cdx_dev->bus_num, cdx_dev->dev_num, &dev_config); 93} 94EXPORT_SYMBOL_GPL(cdx_disable_msi); 95 96static struct irq_chip cdx_msi_irq_chip = { 97 .name = "CDX-MSI", 98 .irq_mask = irq_chip_mask_parent, 99 .irq_unmask = irq_chip_unmask_parent, 100 .irq_eoi = irq_chip_eoi_parent, 101 .irq_set_affinity = msi_domain_set_affinity, 102 .irq_write_msi_msg = cdx_msi_write_msg, 103 .irq_bus_lock = cdx_msi_write_irq_lock, 104 .irq_bus_sync_unlock = cdx_msi_write_irq_unlock 105}; 106 107/* Convert an msi_desc to a unique identifier within the domain. */ 108static irq_hw_number_t cdx_domain_calc_hwirq(struct cdx_device *dev, 109 struct msi_desc *desc) 110{ 111 return ((irq_hw_number_t)dev->msi_dev_id << 10) | desc->msi_index; 112} 113 114static void cdx_msi_set_desc(msi_alloc_info_t *arg, struct msi_desc *desc) 115{ 116 arg->desc = desc; 117 arg->hwirq = cdx_domain_calc_hwirq(to_cdx_device(desc->dev), desc); 118} 119 120static int cdx_msi_prepare(struct irq_domain *msi_domain, 121 struct device *dev, 122 int nvec, msi_alloc_info_t *info) 123{ 124 struct cdx_device *cdx_dev = to_cdx_device(dev); 125 struct device *parent = cdx_dev->cdx->dev; 126 struct msi_domain_info *msi_info; 127 u32 dev_id; 128 int ret; 129 130 /* Retrieve device ID from requestor ID using parent device */ 131 ret = of_map_id(parent->of_node, cdx_dev->msi_dev_id, "msi-map", "msi-map-mask", 132 NULL, &dev_id); 133 if (ret) { 134 dev_err(dev, "of_map_id failed for MSI: %d\n", ret); 135 return ret; 136 } 137 138#ifdef GENERIC_MSI_DOMAIN_OPS 139 /* Set the device Id to be passed to the GIC-ITS */ 140 info->scratchpad[0].ul = dev_id; 141#endif 142 143 msi_info = msi_get_domain_info(msi_domain->parent); 144 145 return msi_info->ops->msi_prepare(msi_domain->parent, dev, nvec, info); 146} 147 148static struct msi_domain_ops cdx_msi_ops = { 149 .msi_prepare = cdx_msi_prepare, 150 .set_desc = cdx_msi_set_desc 151}; 152 153static struct msi_domain_info cdx_msi_domain_info = { 154 .ops = &cdx_msi_ops, 155 .chip = &cdx_msi_irq_chip, 156 .flags = MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | 157 MSI_FLAG_ALLOC_SIMPLE_MSI_DESCS | MSI_FLAG_FREE_MSI_DESCS 158}; 159 160struct irq_domain *cdx_msi_domain_init(struct device *dev) 161{ 162 struct device_node *np = dev->of_node; 163 struct fwnode_handle *fwnode_handle; 164 struct irq_domain *cdx_msi_domain; 165 struct device_node *parent_node; 166 struct irq_domain *parent; 167 168 fwnode_handle = of_node_to_fwnode(np); 169 170 parent_node = of_parse_phandle(np, "msi-map", 1); 171 if (!parent_node) { 172 dev_err(dev, "msi-map not present on cdx controller\n"); 173 return NULL; 174 } 175 176 parent = irq_find_matching_fwnode(of_node_to_fwnode(parent_node), DOMAIN_BUS_NEXUS); 177 if (!parent || !msi_get_domain_info(parent)) { 178 dev_err(dev, "unable to locate ITS domain\n"); 179 return NULL; 180 } 181 182 cdx_msi_domain = msi_create_irq_domain(fwnode_handle, &cdx_msi_domain_info, parent); 183 if (!cdx_msi_domain) { 184 dev_err(dev, "unable to create CDX-MSI domain\n"); 185 return NULL; 186 } 187 188 dev_dbg(dev, "CDX-MSI domain created\n"); 189 190 return cdx_msi_domain; 191} 192EXPORT_SYMBOL_NS_GPL(cdx_msi_domain_init, CDX_BUS_CONTROLLER); 193