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

Configure Feed

Select the types of activity you want to include in your feed.

at v4.5 314 lines 8.1 kB view raw
1/* 2 * Copyright Altera Corporation (C) 2013-2015. All rights reserved 3 * 4 * This program is free software; you can redistribute it and/or modify it 5 * under the terms and conditions of the GNU General Public License, 6 * version 2, as published by the Free Software Foundation. 7 * 8 * This program is distributed in the hope it will be useful, but WITHOUT 9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 11 * more details. 12 * 13 * You should have received a copy of the GNU General Public License along with 14 * this program. If not, see <http://www.gnu.org/licenses/>. 15 */ 16 17#include <linux/interrupt.h> 18#include <linux/irqchip/chained_irq.h> 19#include <linux/module.h> 20#include <linux/msi.h> 21#include <linux/of_address.h> 22#include <linux/of_irq.h> 23#include <linux/of_pci.h> 24#include <linux/pci.h> 25#include <linux/platform_device.h> 26#include <linux/slab.h> 27 28#define MSI_STATUS 0x0 29#define MSI_ERROR 0x4 30#define MSI_INTMASK 0x8 31 32#define MAX_MSI_VECTORS 32 33 34struct altera_msi { 35 DECLARE_BITMAP(used, MAX_MSI_VECTORS); 36 struct mutex lock; /* protect "used" bitmap */ 37 struct platform_device *pdev; 38 struct irq_domain *msi_domain; 39 struct irq_domain *inner_domain; 40 void __iomem *csr_base; 41 void __iomem *vector_base; 42 phys_addr_t vector_phy; 43 u32 num_of_vectors; 44 int irq; 45}; 46 47static inline void msi_writel(struct altera_msi *msi, const u32 value, 48 const u32 reg) 49{ 50 writel_relaxed(value, msi->csr_base + reg); 51} 52 53static inline u32 msi_readl(struct altera_msi *msi, const u32 reg) 54{ 55 return readl_relaxed(msi->csr_base + reg); 56} 57 58static void altera_msi_isr(struct irq_desc *desc) 59{ 60 struct irq_chip *chip = irq_desc_get_chip(desc); 61 struct altera_msi *msi; 62 unsigned long status; 63 u32 num_of_vectors; 64 u32 bit; 65 u32 virq; 66 67 chained_irq_enter(chip, desc); 68 msi = irq_desc_get_handler_data(desc); 69 num_of_vectors = msi->num_of_vectors; 70 71 while ((status = msi_readl(msi, MSI_STATUS)) != 0) { 72 for_each_set_bit(bit, &status, msi->num_of_vectors) { 73 /* Dummy read from vector to clear the interrupt */ 74 readl_relaxed(msi->vector_base + (bit * sizeof(u32))); 75 76 virq = irq_find_mapping(msi->inner_domain, bit); 77 if (virq) 78 generic_handle_irq(virq); 79 else 80 dev_err(&msi->pdev->dev, "unexpected MSI\n"); 81 } 82 } 83 84 chained_irq_exit(chip, desc); 85} 86 87static struct irq_chip altera_msi_irq_chip = { 88 .name = "Altera PCIe MSI", 89 .irq_mask = pci_msi_mask_irq, 90 .irq_unmask = pci_msi_unmask_irq, 91}; 92 93static struct msi_domain_info altera_msi_domain_info = { 94 .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | 95 MSI_FLAG_PCI_MSIX), 96 .chip = &altera_msi_irq_chip, 97}; 98 99static void altera_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) 100{ 101 struct altera_msi *msi = irq_data_get_irq_chip_data(data); 102 phys_addr_t addr = msi->vector_phy + (data->hwirq * sizeof(u32)); 103 104 msg->address_lo = lower_32_bits(addr); 105 msg->address_hi = upper_32_bits(addr); 106 msg->data = data->hwirq; 107 108 dev_dbg(&msi->pdev->dev, "msi#%d address_hi %#x address_lo %#x\n", 109 (int)data->hwirq, msg->address_hi, msg->address_lo); 110} 111 112static int altera_msi_set_affinity(struct irq_data *irq_data, 113 const struct cpumask *mask, bool force) 114{ 115 return -EINVAL; 116} 117 118static struct irq_chip altera_msi_bottom_irq_chip = { 119 .name = "Altera MSI", 120 .irq_compose_msi_msg = altera_compose_msi_msg, 121 .irq_set_affinity = altera_msi_set_affinity, 122}; 123 124static int altera_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, 125 unsigned int nr_irqs, void *args) 126{ 127 struct altera_msi *msi = domain->host_data; 128 unsigned long bit; 129 u32 mask; 130 131 WARN_ON(nr_irqs != 1); 132 mutex_lock(&msi->lock); 133 134 bit = find_first_zero_bit(msi->used, msi->num_of_vectors); 135 if (bit >= msi->num_of_vectors) { 136 mutex_unlock(&msi->lock); 137 return -ENOSPC; 138 } 139 140 set_bit(bit, msi->used); 141 142 mutex_unlock(&msi->lock); 143 144 irq_domain_set_info(domain, virq, bit, &altera_msi_bottom_irq_chip, 145 domain->host_data, handle_simple_irq, 146 NULL, NULL); 147 148 mask = msi_readl(msi, MSI_INTMASK); 149 mask |= 1 << bit; 150 msi_writel(msi, mask, MSI_INTMASK); 151 152 return 0; 153} 154 155static void altera_irq_domain_free(struct irq_domain *domain, 156 unsigned int virq, unsigned int nr_irqs) 157{ 158 struct irq_data *d = irq_domain_get_irq_data(domain, virq); 159 struct altera_msi *msi = irq_data_get_irq_chip_data(d); 160 u32 mask; 161 162 mutex_lock(&msi->lock); 163 164 if (!test_bit(d->hwirq, msi->used)) { 165 dev_err(&msi->pdev->dev, "trying to free unused MSI#%lu\n", 166 d->hwirq); 167 } else { 168 __clear_bit(d->hwirq, msi->used); 169 mask = msi_readl(msi, MSI_INTMASK); 170 mask &= ~(1 << d->hwirq); 171 msi_writel(msi, mask, MSI_INTMASK); 172 } 173 174 mutex_unlock(&msi->lock); 175} 176 177static const struct irq_domain_ops msi_domain_ops = { 178 .alloc = altera_irq_domain_alloc, 179 .free = altera_irq_domain_free, 180}; 181 182static int altera_allocate_domains(struct altera_msi *msi) 183{ 184 struct fwnode_handle *fwnode = of_node_to_fwnode(msi->pdev->dev.of_node); 185 186 msi->inner_domain = irq_domain_add_linear(NULL, msi->num_of_vectors, 187 &msi_domain_ops, msi); 188 if (!msi->inner_domain) { 189 dev_err(&msi->pdev->dev, "failed to create IRQ domain\n"); 190 return -ENOMEM; 191 } 192 193 msi->msi_domain = pci_msi_create_irq_domain(fwnode, 194 &altera_msi_domain_info, msi->inner_domain); 195 if (!msi->msi_domain) { 196 dev_err(&msi->pdev->dev, "failed to create MSI domain\n"); 197 irq_domain_remove(msi->inner_domain); 198 return -ENOMEM; 199 } 200 201 return 0; 202} 203 204static void altera_free_domains(struct altera_msi *msi) 205{ 206 irq_domain_remove(msi->msi_domain); 207 irq_domain_remove(msi->inner_domain); 208} 209 210static int altera_msi_remove(struct platform_device *pdev) 211{ 212 struct altera_msi *msi = platform_get_drvdata(pdev); 213 214 msi_writel(msi, 0, MSI_INTMASK); 215 irq_set_chained_handler(msi->irq, NULL); 216 irq_set_handler_data(msi->irq, NULL); 217 218 altera_free_domains(msi); 219 220 platform_set_drvdata(pdev, NULL); 221 return 0; 222} 223 224static int altera_msi_probe(struct platform_device *pdev) 225{ 226 struct altera_msi *msi; 227 struct device_node *np = pdev->dev.of_node; 228 struct resource *res; 229 int ret; 230 231 msi = devm_kzalloc(&pdev->dev, sizeof(struct altera_msi), 232 GFP_KERNEL); 233 if (!msi) 234 return -ENOMEM; 235 236 mutex_init(&msi->lock); 237 msi->pdev = pdev; 238 239 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "csr"); 240 if (!res) { 241 dev_err(&pdev->dev, "no csr memory resource defined\n"); 242 return -ENODEV; 243 } 244 245 msi->csr_base = devm_ioremap_resource(&pdev->dev, res); 246 if (IS_ERR(msi->csr_base)) { 247 dev_err(&pdev->dev, "failed to map csr memory\n"); 248 return PTR_ERR(msi->csr_base); 249 } 250 251 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, 252 "vector_slave"); 253 if (!res) { 254 dev_err(&pdev->dev, "no vector_slave memory resource defined\n"); 255 return -ENODEV; 256 } 257 258 msi->vector_base = devm_ioremap_resource(&pdev->dev, res); 259 if (IS_ERR(msi->vector_base)) { 260 dev_err(&pdev->dev, "failed to map vector_slave memory\n"); 261 return PTR_ERR(msi->vector_base); 262 } 263 264 msi->vector_phy = res->start; 265 266 if (of_property_read_u32(np, "num-vectors", &msi->num_of_vectors)) { 267 dev_err(&pdev->dev, "failed to parse the number of vectors\n"); 268 return -EINVAL; 269 } 270 271 ret = altera_allocate_domains(msi); 272 if (ret) 273 return ret; 274 275 msi->irq = platform_get_irq(pdev, 0); 276 if (msi->irq <= 0) { 277 dev_err(&pdev->dev, "failed to map IRQ: %d\n", msi->irq); 278 ret = -ENODEV; 279 goto err; 280 } 281 282 irq_set_chained_handler_and_data(msi->irq, altera_msi_isr, msi); 283 platform_set_drvdata(pdev, msi); 284 285 return 0; 286 287err: 288 altera_msi_remove(pdev); 289 return ret; 290} 291 292static const struct of_device_id altera_msi_of_match[] = { 293 { .compatible = "altr,msi-1.0", NULL }, 294 { }, 295}; 296 297static struct platform_driver altera_msi_driver = { 298 .driver = { 299 .name = "altera-msi", 300 .of_match_table = altera_msi_of_match, 301 }, 302 .probe = altera_msi_probe, 303 .remove = altera_msi_remove, 304}; 305 306static int __init altera_msi_init(void) 307{ 308 return platform_driver_register(&altera_msi_driver); 309} 310subsys_initcall(altera_msi_init); 311 312MODULE_AUTHOR("Ley Foon Tan <lftan@altera.com>"); 313MODULE_DESCRIPTION("Altera PCIe MSI support"); 314MODULE_LICENSE("GPL v2");