1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Freescale Management Complex (MC) bus driver MSI support 4 * 5 * Copyright (C) 2015-2016 Freescale Semiconductor, Inc. 6 * Author: German Rivera <German.Rivera@freescale.com> 7 * 8 */ 9 10 #include <linux/of_irq.h> 11 #include <linux/irq.h> 12 #include <linux/irqdomain.h> 13 #include <linux/msi.h> 14 #include <linux/acpi_iort.h> 15 16 #include "fsl-mc-private.h" 17 18 #ifdef GENERIC_MSI_DOMAIN_OPS 19 /* 20 * Generate a unique ID identifying the interrupt (only used within the MSI 21 * irqdomain. Combine the icid with the interrupt index. 22 */ 23 static irq_hw_number_t fsl_mc_domain_calc_hwirq(struct fsl_mc_device *dev, 24 struct msi_desc *desc) 25 { 26 /* 27 * Make the base hwirq value for ICID*10000 so it is readable 28 * as a decimal value in /proc/interrupts. 29 */ 30 return (irq_hw_number_t)(desc->msi_index + (dev->icid * 10000)); 31 } 32 33 static void fsl_mc_msi_set_desc(msi_alloc_info_t *arg, 34 struct msi_desc *desc) 35 { 36 arg->desc = desc; 37 arg->hwirq = fsl_mc_domain_calc_hwirq(to_fsl_mc_device(desc->dev), 38 desc); 39 } 40 #else 41 #define fsl_mc_msi_set_desc NULL 42 #endif 43 44 static void fsl_mc_msi_update_dom_ops(struct msi_domain_info *info) 45 { 46 struct msi_domain_ops *ops = info->ops; 47 48 if (!ops) 49 return; 50 51 /* 52 * set_desc should not be set by the caller 53 */ 54 if (!ops->set_desc) 55 ops->set_desc = fsl_mc_msi_set_desc; 56 } 57 58 static void __fsl_mc_msi_write_msg(struct fsl_mc_device *mc_bus_dev, 59 struct fsl_mc_device_irq *mc_dev_irq, 60 struct msi_desc *msi_desc) 61 { 62 int error; 63 struct fsl_mc_device *owner_mc_dev = mc_dev_irq->mc_dev; 64 struct dprc_irq_cfg irq_cfg; 65 66 /* 67 * msi_desc->msg.address is 0x0 when this function is invoked in 68 * the free_irq() code path. In this case, for the MC, we don't 69 * really need to "unprogram" the MSI, so we just return. 70 */ 71 if (msi_desc->msg.address_lo == 0x0 && msi_desc->msg.address_hi == 0x0) 72 return; 73 74 if (!owner_mc_dev) 75 return; 76 77 irq_cfg.paddr = ((u64)msi_desc->msg.address_hi << 32) | 78 msi_desc->msg.address_lo; 79 irq_cfg.val = msi_desc->msg.data; 80 irq_cfg.irq_num = msi_desc->irq; 81 82 if (owner_mc_dev == mc_bus_dev) { 83 /* 84 * IRQ is for the mc_bus_dev's DPRC itself 85 */ 86 error = dprc_set_irq(mc_bus_dev->mc_io, 87 MC_CMD_FLAG_INTR_DIS | MC_CMD_FLAG_PRI, 88 mc_bus_dev->mc_handle, 89 mc_dev_irq->dev_irq_index, 90 &irq_cfg); 91 if (error < 0) { 92 dev_err(&owner_mc_dev->dev, 93 "dprc_set_irq() failed: %d\n", error); 94 } 95 } else { 96 /* 97 * IRQ is for for a child device of mc_bus_dev 98 */ 99 error = dprc_set_obj_irq(mc_bus_dev->mc_io, 100 MC_CMD_FLAG_INTR_DIS | MC_CMD_FLAG_PRI, 101 mc_bus_dev->mc_handle, 102 owner_mc_dev->obj_desc.type, 103 owner_mc_dev->obj_desc.id, 104 mc_dev_irq->dev_irq_index, 105 &irq_cfg); 106 if (error < 0) { 107 dev_err(&owner_mc_dev->dev, 108 "dprc_obj_set_irq() failed: %d\n", error); 109 } 110 } 111 } 112 113 /* 114 * NOTE: This function is invoked with interrupts disabled 115 */ 116 static void fsl_mc_msi_write_msg(struct irq_data *irq_data, 117 struct msi_msg *msg) 118 { 119 struct msi_desc *msi_desc = irq_data_get_msi_desc(irq_data); 120 struct fsl_mc_device *mc_bus_dev = to_fsl_mc_device(msi_desc->dev); 121 struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev); 122 struct fsl_mc_device_irq *mc_dev_irq = 123 &mc_bus->irq_resources[msi_desc->msi_index]; 124 125 msi_desc->msg = *msg; 126 127 /* 128 * Program the MSI (paddr, value) pair in the device: 129 */ 130 __fsl_mc_msi_write_msg(mc_bus_dev, mc_dev_irq, msi_desc); 131 } 132 133 static void fsl_mc_msi_update_chip_ops(struct msi_domain_info *info) 134 { 135 struct irq_chip *chip = info->chip; 136 137 if (!chip) 138 return; 139 140 /* 141 * irq_write_msi_msg should not be set by the caller 142 */ 143 if (!chip->irq_write_msi_msg) 144 chip->irq_write_msi_msg = fsl_mc_msi_write_msg; 145 } 146 147 /** 148 * fsl_mc_msi_create_irq_domain - Create a fsl-mc MSI interrupt domain 149 * @fwnode: Optional firmware node of the interrupt controller 150 * @info: MSI domain info 151 * @parent: Parent irq domain 152 * 153 * Updates the domain and chip ops and creates a fsl-mc MSI 154 * interrupt domain. 155 * 156 * Returns: 157 * A domain pointer or NULL in case of failure. 158 */ 159 struct irq_domain *fsl_mc_msi_create_irq_domain(struct fwnode_handle *fwnode, 160 struct msi_domain_info *info, 161 struct irq_domain *parent) 162 { 163 struct irq_domain *domain; 164 165 if (WARN_ON((info->flags & MSI_FLAG_LEVEL_CAPABLE))) 166 info->flags &= ~MSI_FLAG_LEVEL_CAPABLE; 167 if (info->flags & MSI_FLAG_USE_DEF_DOM_OPS) 168 fsl_mc_msi_update_dom_ops(info); 169 if (info->flags & MSI_FLAG_USE_DEF_CHIP_OPS) 170 fsl_mc_msi_update_chip_ops(info); 171 info->flags |= MSI_FLAG_ALLOC_SIMPLE_MSI_DESCS | MSI_FLAG_FREE_MSI_DESCS; 172 173 domain = msi_create_irq_domain(fwnode, info, parent); 174 if (domain) 175 irq_domain_update_bus_token(domain, DOMAIN_BUS_FSL_MC_MSI); 176 177 return domain; 178 } 179 180 struct irq_domain *fsl_mc_find_msi_domain(struct device *dev) 181 { 182 struct device *root_dprc_dev; 183 struct device *bus_dev; 184 struct irq_domain *msi_domain; 185 struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); 186 187 fsl_mc_get_root_dprc(dev, &root_dprc_dev); 188 bus_dev = root_dprc_dev->parent; 189 190 if (bus_dev->of_node) { 191 msi_domain = of_msi_map_get_device_domain(dev, 192 mc_dev->icid, 193 DOMAIN_BUS_FSL_MC_MSI); 194 195 /* 196 * if the msi-map property is missing assume that all the 197 * child containers inherit the domain from the parent 198 */ 199 if (!msi_domain) 200 201 msi_domain = of_msi_get_domain(bus_dev, 202 bus_dev->of_node, 203 DOMAIN_BUS_FSL_MC_MSI); 204 } else { 205 msi_domain = iort_get_device_domain(dev, mc_dev->icid, 206 DOMAIN_BUS_FSL_MC_MSI); 207 } 208 209 return msi_domain; 210 } 211 212 int fsl_mc_msi_domain_alloc_irqs(struct device *dev, unsigned int irq_count) 213 { 214 int error = msi_setup_device_data(dev); 215 216 if (error) 217 return error; 218 219 /* 220 * NOTE: Calling this function will trigger the invocation of the 221 * its_fsl_mc_msi_prepare() callback 222 */ 223 error = msi_domain_alloc_irqs_range(dev, MSI_DEFAULT_DOMAIN, 0, irq_count - 1); 224 225 if (error) 226 dev_err(dev, "Failed to allocate IRQs\n"); 227 return error; 228 } 229 230 void fsl_mc_msi_domain_free_irqs(struct device *dev) 231 { 232 msi_domain_free_irqs_all(dev, MSI_DEFAULT_DOMAIN); 233 } 234