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 v3.0 457 lines 11 kB view raw
1/* 2 * Copyright (C) 2007-2011 Freescale Semiconductor, Inc. 3 * 4 * Author: Tony Li <tony.li@freescale.com> 5 * Jason Jin <Jason.jin@freescale.com> 6 * 7 * The hwirq alloc and free code reuse from sysdev/mpic_msi.c 8 * 9 * This program is free software; you can redistribute it and/or 10 * modify it under the terms of the GNU General Public License 11 * as published by the Free Software Foundation; version 2 of the 12 * License. 13 * 14 */ 15#include <linux/irq.h> 16#include <linux/bootmem.h> 17#include <linux/msi.h> 18#include <linux/pci.h> 19#include <linux/slab.h> 20#include <linux/of_platform.h> 21#include <sysdev/fsl_soc.h> 22#include <asm/prom.h> 23#include <asm/hw_irq.h> 24#include <asm/ppc-pci.h> 25#include <asm/mpic.h> 26#include "fsl_msi.h" 27#include "fsl_pci.h" 28 29LIST_HEAD(msi_head); 30 31struct fsl_msi_feature { 32 u32 fsl_pic_ip; 33 u32 msiir_offset; 34}; 35 36struct fsl_msi_cascade_data { 37 struct fsl_msi *msi_data; 38 int index; 39}; 40 41static inline u32 fsl_msi_read(u32 __iomem *base, unsigned int reg) 42{ 43 return in_be32(base + (reg >> 2)); 44} 45 46/* 47 * We do not need this actually. The MSIR register has been read once 48 * in the cascade interrupt. So, this MSI interrupt has been acked 49*/ 50static void fsl_msi_end_irq(struct irq_data *d) 51{ 52} 53 54static struct irq_chip fsl_msi_chip = { 55 .irq_mask = mask_msi_irq, 56 .irq_unmask = unmask_msi_irq, 57 .irq_ack = fsl_msi_end_irq, 58 .name = "FSL-MSI", 59}; 60 61static int fsl_msi_host_map(struct irq_host *h, unsigned int virq, 62 irq_hw_number_t hw) 63{ 64 struct fsl_msi *msi_data = h->host_data; 65 struct irq_chip *chip = &fsl_msi_chip; 66 67 irq_set_status_flags(virq, IRQ_TYPE_EDGE_FALLING); 68 69 irq_set_chip_data(virq, msi_data); 70 irq_set_chip_and_handler(virq, chip, handle_edge_irq); 71 72 return 0; 73} 74 75static struct irq_host_ops fsl_msi_host_ops = { 76 .map = fsl_msi_host_map, 77}; 78 79static int fsl_msi_init_allocator(struct fsl_msi *msi_data) 80{ 81 int rc; 82 83 rc = msi_bitmap_alloc(&msi_data->bitmap, NR_MSI_IRQS, 84 msi_data->irqhost->of_node); 85 if (rc) 86 return rc; 87 88 rc = msi_bitmap_reserve_dt_hwirqs(&msi_data->bitmap); 89 if (rc < 0) { 90 msi_bitmap_free(&msi_data->bitmap); 91 return rc; 92 } 93 94 return 0; 95} 96 97static int fsl_msi_check_device(struct pci_dev *pdev, int nvec, int type) 98{ 99 if (type == PCI_CAP_ID_MSIX) 100 pr_debug("fslmsi: MSI-X untested, trying anyway.\n"); 101 102 return 0; 103} 104 105static void fsl_teardown_msi_irqs(struct pci_dev *pdev) 106{ 107 struct msi_desc *entry; 108 struct fsl_msi *msi_data; 109 110 list_for_each_entry(entry, &pdev->msi_list, list) { 111 if (entry->irq == NO_IRQ) 112 continue; 113 msi_data = irq_get_chip_data(entry->irq); 114 irq_set_msi_desc(entry->irq, NULL); 115 msi_bitmap_free_hwirqs(&msi_data->bitmap, 116 virq_to_hw(entry->irq), 1); 117 irq_dispose_mapping(entry->irq); 118 } 119 120 return; 121} 122 123static void fsl_compose_msi_msg(struct pci_dev *pdev, int hwirq, 124 struct msi_msg *msg, 125 struct fsl_msi *fsl_msi_data) 126{ 127 struct fsl_msi *msi_data = fsl_msi_data; 128 struct pci_controller *hose = pci_bus_to_host(pdev->bus); 129 u64 base = fsl_pci_immrbar_base(hose); 130 131 msg->address_lo = msi_data->msi_addr_lo + lower_32_bits(base); 132 msg->address_hi = msi_data->msi_addr_hi + upper_32_bits(base); 133 134 msg->data = hwirq; 135 136 pr_debug("%s: allocated srs: %d, ibs: %d\n", 137 __func__, hwirq / IRQS_PER_MSI_REG, hwirq % IRQS_PER_MSI_REG); 138} 139 140static int fsl_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type) 141{ 142 int rc, hwirq = -ENOMEM; 143 unsigned int virq; 144 struct msi_desc *entry; 145 struct msi_msg msg; 146 struct fsl_msi *msi_data; 147 148 list_for_each_entry(entry, &pdev->msi_list, list) { 149 list_for_each_entry(msi_data, &msi_head, list) { 150 hwirq = msi_bitmap_alloc_hwirqs(&msi_data->bitmap, 1); 151 if (hwirq >= 0) 152 break; 153 } 154 155 if (hwirq < 0) { 156 rc = hwirq; 157 pr_debug("%s: fail allocating msi interrupt\n", 158 __func__); 159 goto out_free; 160 } 161 162 virq = irq_create_mapping(msi_data->irqhost, hwirq); 163 164 if (virq == NO_IRQ) { 165 pr_debug("%s: fail mapping hwirq 0x%x\n", 166 __func__, hwirq); 167 msi_bitmap_free_hwirqs(&msi_data->bitmap, hwirq, 1); 168 rc = -ENOSPC; 169 goto out_free; 170 } 171 /* chip_data is msi_data via host->hostdata in host->map() */ 172 irq_set_msi_desc(virq, entry); 173 174 fsl_compose_msi_msg(pdev, hwirq, &msg, msi_data); 175 write_msi_msg(virq, &msg); 176 } 177 return 0; 178 179out_free: 180 /* free by the caller of this function */ 181 return rc; 182} 183 184static void fsl_msi_cascade(unsigned int irq, struct irq_desc *desc) 185{ 186 struct irq_chip *chip = irq_desc_get_chip(desc); 187 struct irq_data *idata = irq_desc_get_irq_data(desc); 188 unsigned int cascade_irq; 189 struct fsl_msi *msi_data; 190 int msir_index = -1; 191 u32 msir_value = 0; 192 u32 intr_index; 193 u32 have_shift = 0; 194 struct fsl_msi_cascade_data *cascade_data; 195 196 cascade_data = irq_get_handler_data(irq); 197 msi_data = cascade_data->msi_data; 198 199 raw_spin_lock(&desc->lock); 200 if ((msi_data->feature & FSL_PIC_IP_MASK) == FSL_PIC_IP_IPIC) { 201 if (chip->irq_mask_ack) 202 chip->irq_mask_ack(idata); 203 else { 204 chip->irq_mask(idata); 205 chip->irq_ack(idata); 206 } 207 } 208 209 if (unlikely(irqd_irq_inprogress(idata))) 210 goto unlock; 211 212 msir_index = cascade_data->index; 213 214 if (msir_index >= NR_MSI_REG) 215 cascade_irq = NO_IRQ; 216 217 irqd_set_chained_irq_inprogress(idata); 218 switch (msi_data->feature & FSL_PIC_IP_MASK) { 219 case FSL_PIC_IP_MPIC: 220 msir_value = fsl_msi_read(msi_data->msi_regs, 221 msir_index * 0x10); 222 break; 223 case FSL_PIC_IP_IPIC: 224 msir_value = fsl_msi_read(msi_data->msi_regs, msir_index * 0x4); 225 break; 226 } 227 228 while (msir_value) { 229 intr_index = ffs(msir_value) - 1; 230 231 cascade_irq = irq_linear_revmap(msi_data->irqhost, 232 msir_index * IRQS_PER_MSI_REG + 233 intr_index + have_shift); 234 if (cascade_irq != NO_IRQ) 235 generic_handle_irq(cascade_irq); 236 have_shift += intr_index + 1; 237 msir_value = msir_value >> (intr_index + 1); 238 } 239 irqd_clr_chained_irq_inprogress(idata); 240 241 switch (msi_data->feature & FSL_PIC_IP_MASK) { 242 case FSL_PIC_IP_MPIC: 243 chip->irq_eoi(idata); 244 break; 245 case FSL_PIC_IP_IPIC: 246 if (!irqd_irq_disabled(idata) && chip->irq_unmask) 247 chip->irq_unmask(idata); 248 break; 249 } 250unlock: 251 raw_spin_unlock(&desc->lock); 252} 253 254static int fsl_of_msi_remove(struct platform_device *ofdev) 255{ 256 struct fsl_msi *msi = platform_get_drvdata(ofdev); 257 int virq, i; 258 struct fsl_msi_cascade_data *cascade_data; 259 260 if (msi->list.prev != NULL) 261 list_del(&msi->list); 262 for (i = 0; i < NR_MSI_REG; i++) { 263 virq = msi->msi_virqs[i]; 264 if (virq != NO_IRQ) { 265 cascade_data = irq_get_handler_data(virq); 266 kfree(cascade_data); 267 irq_dispose_mapping(virq); 268 } 269 } 270 if (msi->bitmap.bitmap) 271 msi_bitmap_free(&msi->bitmap); 272 iounmap(msi->msi_regs); 273 kfree(msi); 274 275 return 0; 276} 277 278static int __devinit fsl_msi_setup_hwirq(struct fsl_msi *msi, 279 struct platform_device *dev, 280 int offset, int irq_index) 281{ 282 struct fsl_msi_cascade_data *cascade_data = NULL; 283 int virt_msir; 284 285 virt_msir = irq_of_parse_and_map(dev->dev.of_node, irq_index); 286 if (virt_msir == NO_IRQ) { 287 dev_err(&dev->dev, "%s: Cannot translate IRQ index %d\n", 288 __func__, irq_index); 289 return 0; 290 } 291 292 cascade_data = kzalloc(sizeof(struct fsl_msi_cascade_data), GFP_KERNEL); 293 if (!cascade_data) { 294 dev_err(&dev->dev, "No memory for MSI cascade data\n"); 295 return -ENOMEM; 296 } 297 298 msi->msi_virqs[irq_index] = virt_msir; 299 cascade_data->index = offset + irq_index; 300 cascade_data->msi_data = msi; 301 irq_set_handler_data(virt_msir, cascade_data); 302 irq_set_chained_handler(virt_msir, fsl_msi_cascade); 303 304 return 0; 305} 306 307static const struct of_device_id fsl_of_msi_ids[]; 308static int __devinit fsl_of_msi_probe(struct platform_device *dev) 309{ 310 const struct of_device_id *match; 311 struct fsl_msi *msi; 312 struct resource res; 313 int err, i, j, irq_index, count; 314 int rc; 315 const u32 *p; 316 struct fsl_msi_feature *features; 317 int len; 318 u32 offset; 319 static const u32 all_avail[] = { 0, NR_MSI_IRQS }; 320 321 match = of_match_device(fsl_of_msi_ids, &dev->dev); 322 if (!match) 323 return -EINVAL; 324 features = match->data; 325 326 printk(KERN_DEBUG "Setting up Freescale MSI support\n"); 327 328 msi = kzalloc(sizeof(struct fsl_msi), GFP_KERNEL); 329 if (!msi) { 330 dev_err(&dev->dev, "No memory for MSI structure\n"); 331 return -ENOMEM; 332 } 333 platform_set_drvdata(dev, msi); 334 335 msi->irqhost = irq_alloc_host(dev->dev.of_node, IRQ_HOST_MAP_LINEAR, 336 NR_MSI_IRQS, &fsl_msi_host_ops, 0); 337 338 if (msi->irqhost == NULL) { 339 dev_err(&dev->dev, "No memory for MSI irqhost\n"); 340 err = -ENOMEM; 341 goto error_out; 342 } 343 344 /* Get the MSI reg base */ 345 err = of_address_to_resource(dev->dev.of_node, 0, &res); 346 if (err) { 347 dev_err(&dev->dev, "%s resource error!\n", 348 dev->dev.of_node->full_name); 349 goto error_out; 350 } 351 352 msi->msi_regs = ioremap(res.start, res.end - res.start + 1); 353 if (!msi->msi_regs) { 354 dev_err(&dev->dev, "ioremap problem failed\n"); 355 goto error_out; 356 } 357 358 msi->feature = features->fsl_pic_ip; 359 360 msi->irqhost->host_data = msi; 361 362 msi->msi_addr_hi = 0x0; 363 msi->msi_addr_lo = features->msiir_offset + (res.start & 0xfffff); 364 365 rc = fsl_msi_init_allocator(msi); 366 if (rc) { 367 dev_err(&dev->dev, "Error allocating MSI bitmap\n"); 368 goto error_out; 369 } 370 371 p = of_get_property(dev->dev.of_node, "msi-available-ranges", &len); 372 if (p && len % (2 * sizeof(u32)) != 0) { 373 dev_err(&dev->dev, "%s: Malformed msi-available-ranges property\n", 374 __func__); 375 err = -EINVAL; 376 goto error_out; 377 } 378 379 if (!p) 380 p = all_avail; 381 382 for (irq_index = 0, i = 0; i < len / (2 * sizeof(u32)); i++) { 383 if (p[i * 2] % IRQS_PER_MSI_REG || 384 p[i * 2 + 1] % IRQS_PER_MSI_REG) { 385 printk(KERN_WARNING "%s: %s: msi available range of %u at %u is not IRQ-aligned\n", 386 __func__, dev->dev.of_node->full_name, 387 p[i * 2 + 1], p[i * 2]); 388 err = -EINVAL; 389 goto error_out; 390 } 391 392 offset = p[i * 2] / IRQS_PER_MSI_REG; 393 count = p[i * 2 + 1] / IRQS_PER_MSI_REG; 394 395 for (j = 0; j < count; j++, irq_index++) { 396 err = fsl_msi_setup_hwirq(msi, dev, offset, irq_index); 397 if (err) 398 goto error_out; 399 } 400 } 401 402 list_add_tail(&msi->list, &msi_head); 403 404 /* The multiple setting ppc_md.setup_msi_irqs will not harm things */ 405 if (!ppc_md.setup_msi_irqs) { 406 ppc_md.setup_msi_irqs = fsl_setup_msi_irqs; 407 ppc_md.teardown_msi_irqs = fsl_teardown_msi_irqs; 408 ppc_md.msi_check_device = fsl_msi_check_device; 409 } else if (ppc_md.setup_msi_irqs != fsl_setup_msi_irqs) { 410 dev_err(&dev->dev, "Different MSI driver already installed!\n"); 411 err = -ENODEV; 412 goto error_out; 413 } 414 return 0; 415error_out: 416 fsl_of_msi_remove(dev); 417 return err; 418} 419 420static const struct fsl_msi_feature mpic_msi_feature = { 421 .fsl_pic_ip = FSL_PIC_IP_MPIC, 422 .msiir_offset = 0x140, 423}; 424 425static const struct fsl_msi_feature ipic_msi_feature = { 426 .fsl_pic_ip = FSL_PIC_IP_IPIC, 427 .msiir_offset = 0x38, 428}; 429 430static const struct of_device_id fsl_of_msi_ids[] = { 431 { 432 .compatible = "fsl,mpic-msi", 433 .data = (void *)&mpic_msi_feature, 434 }, 435 { 436 .compatible = "fsl,ipic-msi", 437 .data = (void *)&ipic_msi_feature, 438 }, 439 {} 440}; 441 442static struct platform_driver fsl_of_msi_driver = { 443 .driver = { 444 .name = "fsl-msi", 445 .owner = THIS_MODULE, 446 .of_match_table = fsl_of_msi_ids, 447 }, 448 .probe = fsl_of_msi_probe, 449 .remove = fsl_of_msi_remove, 450}; 451 452static __init int fsl_of_msi_init(void) 453{ 454 return platform_driver_register(&fsl_of_msi_driver); 455} 456 457subsys_initcall(fsl_of_msi_init);