1 /* 2 * Freescale SCFG MSI(-X) support 3 * 4 * Copyright (C) 2016 Freescale Semiconductor. 5 * 6 * Author: Minghuan Lian <Minghuan.Lian@nxp.com> 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License version 2 as 10 * published by the Free Software Foundation. 11 */ 12 13 #include <linux/kernel.h> 14 #include <linux/module.h> 15 #include <linux/msi.h> 16 #include <linux/interrupt.h> 17 #include <linux/irq.h> 18 #include <linux/irqchip/chained_irq.h> 19 #include <linux/irqdomain.h> 20 #include <linux/of_pci.h> 21 #include <linux/of_platform.h> 22 #include <linux/spinlock.h> 23 24 #define MSI_MAX_IRQS 32 25 #define MSI_IBS_SHIFT 3 26 #define MSIR 4 27 28 struct ls_scfg_msi { 29 spinlock_t lock; 30 struct platform_device *pdev; 31 struct irq_domain *parent; 32 struct irq_domain *msi_domain; 33 void __iomem *regs; 34 phys_addr_t msiir_addr; 35 int irq; 36 DECLARE_BITMAP(used, MSI_MAX_IRQS); 37 }; 38 39 static struct irq_chip ls_scfg_msi_irq_chip = { 40 .name = "MSI", 41 .irq_mask = pci_msi_mask_irq, 42 .irq_unmask = pci_msi_unmask_irq, 43 }; 44 45 static struct msi_domain_info ls_scfg_msi_domain_info = { 46 .flags = (MSI_FLAG_USE_DEF_DOM_OPS | 47 MSI_FLAG_USE_DEF_CHIP_OPS | 48 MSI_FLAG_PCI_MSIX), 49 .chip = &ls_scfg_msi_irq_chip, 50 }; 51 52 static void ls_scfg_msi_compose_msg(struct irq_data *data, struct msi_msg *msg) 53 { 54 struct ls_scfg_msi *msi_data = irq_data_get_irq_chip_data(data); 55 56 msg->address_hi = upper_32_bits(msi_data->msiir_addr); 57 msg->address_lo = lower_32_bits(msi_data->msiir_addr); 58 msg->data = data->hwirq << MSI_IBS_SHIFT; 59 } 60 61 static int ls_scfg_msi_set_affinity(struct irq_data *irq_data, 62 const struct cpumask *mask, bool force) 63 { 64 return -EINVAL; 65 } 66 67 static struct irq_chip ls_scfg_msi_parent_chip = { 68 .name = "SCFG", 69 .irq_compose_msi_msg = ls_scfg_msi_compose_msg, 70 .irq_set_affinity = ls_scfg_msi_set_affinity, 71 }; 72 73 static int ls_scfg_msi_domain_irq_alloc(struct irq_domain *domain, 74 unsigned int virq, 75 unsigned int nr_irqs, 76 void *args) 77 { 78 struct ls_scfg_msi *msi_data = domain->host_data; 79 int pos, err = 0; 80 81 WARN_ON(nr_irqs != 1); 82 83 spin_lock(&msi_data->lock); 84 pos = find_first_zero_bit(msi_data->used, MSI_MAX_IRQS); 85 if (pos < MSI_MAX_IRQS) 86 __set_bit(pos, msi_data->used); 87 else 88 err = -ENOSPC; 89 spin_unlock(&msi_data->lock); 90 91 if (err) 92 return err; 93 94 irq_domain_set_info(domain, virq, pos, 95 &ls_scfg_msi_parent_chip, msi_data, 96 handle_simple_irq, NULL, NULL); 97 98 return 0; 99 } 100 101 static void ls_scfg_msi_domain_irq_free(struct irq_domain *domain, 102 unsigned int virq, unsigned int nr_irqs) 103 { 104 struct irq_data *d = irq_domain_get_irq_data(domain, virq); 105 struct ls_scfg_msi *msi_data = irq_data_get_irq_chip_data(d); 106 int pos; 107 108 pos = d->hwirq; 109 if (pos < 0 || pos >= MSI_MAX_IRQS) { 110 pr_err("failed to teardown msi. Invalid hwirq %d\n", pos); 111 return; 112 } 113 114 spin_lock(&msi_data->lock); 115 __clear_bit(pos, msi_data->used); 116 spin_unlock(&msi_data->lock); 117 } 118 119 static const struct irq_domain_ops ls_scfg_msi_domain_ops = { 120 .alloc = ls_scfg_msi_domain_irq_alloc, 121 .free = ls_scfg_msi_domain_irq_free, 122 }; 123 124 static void ls_scfg_msi_irq_handler(struct irq_desc *desc) 125 { 126 struct ls_scfg_msi *msi_data = irq_desc_get_handler_data(desc); 127 unsigned long val; 128 int pos, virq; 129 130 chained_irq_enter(irq_desc_get_chip(desc), desc); 131 132 val = ioread32be(msi_data->regs + MSIR); 133 for_each_set_bit(pos, &val, MSI_MAX_IRQS) { 134 virq = irq_find_mapping(msi_data->parent, (31 - pos)); 135 if (virq) 136 generic_handle_irq(virq); 137 } 138 139 chained_irq_exit(irq_desc_get_chip(desc), desc); 140 } 141 142 static int ls_scfg_msi_domains_init(struct ls_scfg_msi *msi_data) 143 { 144 /* Initialize MSI domain parent */ 145 msi_data->parent = irq_domain_add_linear(NULL, 146 MSI_MAX_IRQS, 147 &ls_scfg_msi_domain_ops, 148 msi_data); 149 if (!msi_data->parent) { 150 dev_err(&msi_data->pdev->dev, "failed to create IRQ domain\n"); 151 return -ENOMEM; 152 } 153 154 msi_data->msi_domain = pci_msi_create_irq_domain( 155 of_node_to_fwnode(msi_data->pdev->dev.of_node), 156 &ls_scfg_msi_domain_info, 157 msi_data->parent); 158 if (!msi_data->msi_domain) { 159 dev_err(&msi_data->pdev->dev, "failed to create MSI domain\n"); 160 irq_domain_remove(msi_data->parent); 161 return -ENOMEM; 162 } 163 164 return 0; 165 } 166 167 static int ls_scfg_msi_probe(struct platform_device *pdev) 168 { 169 struct ls_scfg_msi *msi_data; 170 struct resource *res; 171 int ret; 172 173 msi_data = devm_kzalloc(&pdev->dev, sizeof(*msi_data), GFP_KERNEL); 174 if (!msi_data) 175 return -ENOMEM; 176 177 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 178 msi_data->regs = devm_ioremap_resource(&pdev->dev, res); 179 if (IS_ERR(msi_data->regs)) { 180 dev_err(&pdev->dev, "failed to initialize 'regs'\n"); 181 return PTR_ERR(msi_data->regs); 182 } 183 msi_data->msiir_addr = res->start; 184 185 msi_data->irq = platform_get_irq(pdev, 0); 186 if (msi_data->irq <= 0) { 187 dev_err(&pdev->dev, "failed to get MSI irq\n"); 188 return -ENODEV; 189 } 190 191 msi_data->pdev = pdev; 192 spin_lock_init(&msi_data->lock); 193 194 ret = ls_scfg_msi_domains_init(msi_data); 195 if (ret) 196 return ret; 197 198 irq_set_chained_handler_and_data(msi_data->irq, 199 ls_scfg_msi_irq_handler, 200 msi_data); 201 202 platform_set_drvdata(pdev, msi_data); 203 204 return 0; 205 } 206 207 static int ls_scfg_msi_remove(struct platform_device *pdev) 208 { 209 struct ls_scfg_msi *msi_data = platform_get_drvdata(pdev); 210 211 irq_set_chained_handler_and_data(msi_data->irq, NULL, NULL); 212 213 irq_domain_remove(msi_data->msi_domain); 214 irq_domain_remove(msi_data->parent); 215 216 platform_set_drvdata(pdev, NULL); 217 218 return 0; 219 } 220 221 static const struct of_device_id ls_scfg_msi_id[] = { 222 { .compatible = "fsl,1s1021a-msi", }, 223 { .compatible = "fsl,1s1043a-msi", }, 224 {}, 225 }; 226 227 static struct platform_driver ls_scfg_msi_driver = { 228 .driver = { 229 .name = "ls-scfg-msi", 230 .of_match_table = ls_scfg_msi_id, 231 }, 232 .probe = ls_scfg_msi_probe, 233 .remove = ls_scfg_msi_remove, 234 }; 235 236 module_platform_driver(ls_scfg_msi_driver); 237 238 MODULE_AUTHOR("Minghuan Lian <Minghuan.Lian@nxp.com>"); 239 MODULE_DESCRIPTION("Freescale Layerscape SCFG MSI controller driver"); 240 MODULE_LICENSE("GPL v2"); 241