Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Support for 'media5200-platform' compatible boards.
0004  *
0005  * Copyright (C) 2008 Secret Lab Technologies Ltd.
0006  *
0007  * Description:
0008  * This code implements support for the Freescape Media5200 platform
0009  * (built around the MPC5200 SoC).
0010  *
0011  * Notable characteristic of the Media5200 is the presence of an FPGA
0012  * that has all external IRQ lines routed through it.  This file implements
0013  * a cascaded interrupt controller driver which attaches itself to the
0014  * Virtual IRQ subsystem after the primary mpc5200 interrupt controller
0015  * is initialized.
0016  */
0017 
0018 #undef DEBUG
0019 
0020 #include <linux/irq.h>
0021 #include <linux/interrupt.h>
0022 #include <linux/io.h>
0023 #include <linux/of_address.h>
0024 #include <linux/of_irq.h>
0025 #include <asm/time.h>
0026 #include <asm/machdep.h>
0027 #include <asm/mpc52xx.h>
0028 
0029 static const struct of_device_id mpc5200_gpio_ids[] __initconst = {
0030     { .compatible = "fsl,mpc5200-gpio", },
0031     { .compatible = "mpc5200-gpio", },
0032     {}
0033 };
0034 
0035 /* FPGA register set */
0036 #define MEDIA5200_IRQ_ENABLE (0x40c)
0037 #define MEDIA5200_IRQ_STATUS (0x410)
0038 #define MEDIA5200_NUM_IRQS   (6)
0039 #define MEDIA5200_IRQ_SHIFT  (32 - MEDIA5200_NUM_IRQS)
0040 
0041 struct media5200_irq {
0042     void __iomem *regs;
0043     spinlock_t lock;
0044     struct irq_domain *irqhost;
0045 };
0046 struct media5200_irq media5200_irq;
0047 
0048 static void media5200_irq_unmask(struct irq_data *d)
0049 {
0050     unsigned long flags;
0051     u32 val;
0052 
0053     spin_lock_irqsave(&media5200_irq.lock, flags);
0054     val = in_be32(media5200_irq.regs + MEDIA5200_IRQ_ENABLE);
0055     val |= 1 << (MEDIA5200_IRQ_SHIFT + irqd_to_hwirq(d));
0056     out_be32(media5200_irq.regs + MEDIA5200_IRQ_ENABLE, val);
0057     spin_unlock_irqrestore(&media5200_irq.lock, flags);
0058 }
0059 
0060 static void media5200_irq_mask(struct irq_data *d)
0061 {
0062     unsigned long flags;
0063     u32 val;
0064 
0065     spin_lock_irqsave(&media5200_irq.lock, flags);
0066     val = in_be32(media5200_irq.regs + MEDIA5200_IRQ_ENABLE);
0067     val &= ~(1 << (MEDIA5200_IRQ_SHIFT + irqd_to_hwirq(d)));
0068     out_be32(media5200_irq.regs + MEDIA5200_IRQ_ENABLE, val);
0069     spin_unlock_irqrestore(&media5200_irq.lock, flags);
0070 }
0071 
0072 static struct irq_chip media5200_irq_chip = {
0073     .name = "Media5200 FPGA",
0074     .irq_unmask = media5200_irq_unmask,
0075     .irq_mask = media5200_irq_mask,
0076     .irq_mask_ack = media5200_irq_mask,
0077 };
0078 
0079 static void media5200_irq_cascade(struct irq_desc *desc)
0080 {
0081     struct irq_chip *chip = irq_desc_get_chip(desc);
0082     int val;
0083     u32 status, enable;
0084 
0085     /* Mask off the cascaded IRQ */
0086     raw_spin_lock(&desc->lock);
0087     chip->irq_mask(&desc->irq_data);
0088     raw_spin_unlock(&desc->lock);
0089 
0090     /* Ask the FPGA for IRQ status.  If 'val' is 0, then no irqs
0091      * are pending.  'ffs()' is 1 based */
0092     status = in_be32(media5200_irq.regs + MEDIA5200_IRQ_ENABLE);
0093     enable = in_be32(media5200_irq.regs + MEDIA5200_IRQ_STATUS);
0094     val = ffs((status & enable) >> MEDIA5200_IRQ_SHIFT);
0095     if (val) {
0096         generic_handle_domain_irq(media5200_irq.irqhost, val - 1);
0097         /* pr_debug("%s: virq=%i s=%.8x e=%.8x hwirq=%i\n",
0098          *          __func__, virq, status, enable, val - 1);
0099          */
0100     }
0101 
0102     /* Processing done; can reenable the cascade now */
0103     raw_spin_lock(&desc->lock);
0104     chip->irq_ack(&desc->irq_data);
0105     if (!irqd_irq_disabled(&desc->irq_data))
0106         chip->irq_unmask(&desc->irq_data);
0107     raw_spin_unlock(&desc->lock);
0108 }
0109 
0110 static int media5200_irq_map(struct irq_domain *h, unsigned int virq,
0111                  irq_hw_number_t hw)
0112 {
0113     pr_debug("%s: h=%p, virq=%i, hwirq=%i\n", __func__, h, virq, (int)hw);
0114     irq_set_chip_data(virq, &media5200_irq);
0115     irq_set_chip_and_handler(virq, &media5200_irq_chip, handle_level_irq);
0116     irq_set_status_flags(virq, IRQ_LEVEL);
0117     return 0;
0118 }
0119 
0120 static int media5200_irq_xlate(struct irq_domain *h, struct device_node *ct,
0121                  const u32 *intspec, unsigned int intsize,
0122                  irq_hw_number_t *out_hwirq,
0123                  unsigned int *out_flags)
0124 {
0125     if (intsize != 2)
0126         return -1;
0127 
0128     pr_debug("%s: bank=%i, number=%i\n", __func__, intspec[0], intspec[1]);
0129     *out_hwirq = intspec[1];
0130     *out_flags = IRQ_TYPE_NONE;
0131     return 0;
0132 }
0133 
0134 static const struct irq_domain_ops media5200_irq_ops = {
0135     .map = media5200_irq_map,
0136     .xlate = media5200_irq_xlate,
0137 };
0138 
0139 /*
0140  * Setup Media5200 IRQ mapping
0141  */
0142 static void __init media5200_init_irq(void)
0143 {
0144     struct device_node *fpga_np;
0145     int cascade_virq;
0146 
0147     /* First setup the regular MPC5200 interrupt controller */
0148     mpc52xx_init_irq();
0149 
0150     /* Now find the FPGA IRQ */
0151     fpga_np = of_find_compatible_node(NULL, NULL, "fsl,media5200-fpga");
0152     if (!fpga_np)
0153         goto out;
0154     pr_debug("%s: found fpga node: %pOF\n", __func__, fpga_np);
0155 
0156     media5200_irq.regs = of_iomap(fpga_np, 0);
0157     if (!media5200_irq.regs)
0158         goto out;
0159     pr_debug("%s: mapped to %p\n", __func__, media5200_irq.regs);
0160 
0161     cascade_virq = irq_of_parse_and_map(fpga_np, 0);
0162     if (!cascade_virq)
0163         goto out;
0164     pr_debug("%s: cascaded on virq=%i\n", __func__, cascade_virq);
0165 
0166     /* Disable all FPGA IRQs */
0167     out_be32(media5200_irq.regs + MEDIA5200_IRQ_ENABLE, 0);
0168 
0169     spin_lock_init(&media5200_irq.lock);
0170 
0171     media5200_irq.irqhost = irq_domain_add_linear(fpga_np,
0172             MEDIA5200_NUM_IRQS, &media5200_irq_ops, &media5200_irq);
0173     if (!media5200_irq.irqhost)
0174         goto out;
0175     pr_debug("%s: allocated irqhost\n", __func__);
0176 
0177     irq_set_handler_data(cascade_virq, &media5200_irq);
0178     irq_set_chained_handler(cascade_virq, media5200_irq_cascade);
0179 
0180     return;
0181 
0182  out:
0183     pr_err("Could not find Media5200 FPGA; PCI interrupts will not work\n");
0184 }
0185 
0186 /*
0187  * Setup the architecture
0188  */
0189 static void __init media5200_setup_arch(void)
0190 {
0191 
0192     struct device_node *np;
0193     struct mpc52xx_gpio __iomem *gpio;
0194     u32 port_config;
0195 
0196     if (ppc_md.progress)
0197         ppc_md.progress("media5200_setup_arch()", 0);
0198 
0199     /* Map important registers from the internal memory map */
0200     mpc52xx_map_common_devices();
0201 
0202     /* Some mpc5200 & mpc5200b related configuration */
0203     mpc5200_setup_xlb_arbiter();
0204 
0205     np = of_find_matching_node(NULL, mpc5200_gpio_ids);
0206     gpio = of_iomap(np, 0);
0207     of_node_put(np);
0208     if (!gpio) {
0209         printk(KERN_ERR "%s() failed. expect abnormal behavior\n",
0210                __func__);
0211         return;
0212     }
0213 
0214     /* Set port config */
0215     port_config = in_be32(&gpio->port_config);
0216 
0217     port_config &= ~0x03000000; /* ATA CS is on csb_4/5     */
0218     port_config |=  0x01000000;
0219 
0220     out_be32(&gpio->port_config, port_config);
0221 
0222     /* Unmap zone */
0223     iounmap(gpio);
0224 
0225 }
0226 
0227 /* list of the supported boards */
0228 static const char * const board[] __initconst = {
0229     "fsl,media5200",
0230     NULL
0231 };
0232 
0233 /*
0234  * Called very early, MMU is off, device-tree isn't unflattened
0235  */
0236 static int __init media5200_probe(void)
0237 {
0238     return of_device_compatible_match(of_root, board);
0239 }
0240 
0241 define_machine(media5200_platform) {
0242     .name       = "media5200-platform",
0243     .probe      = media5200_probe,
0244     .setup_arch = media5200_setup_arch,
0245     .discover_phbs  = mpc52xx_setup_pci,
0246     .init       = mpc52xx_declare_of_platform_devices,
0247     .init_IRQ   = media5200_init_irq,
0248     .get_irq    = mpc52xx_get_irq,
0249     .restart    = mpc52xx_restart,
0250     .calibrate_decr = generic_calibrate_decr,
0251 };