Path: blob/master/arch/powerpc/platforms/52xx/media5200.c
26481 views
// SPDX-License-Identifier: GPL-2.0-or-later1/*2* Support for 'media5200-platform' compatible boards.3*4* Copyright (C) 2008 Secret Lab Technologies Ltd.5*6* Description:7* This code implements support for the Freescape Media5200 platform8* (built around the MPC5200 SoC).9*10* Notable characteristic of the Media5200 is the presence of an FPGA11* that has all external IRQ lines routed through it. This file implements12* a cascaded interrupt controller driver which attaches itself to the13* Virtual IRQ subsystem after the primary mpc5200 interrupt controller14* is initialized.15*/1617#undef DEBUG1819#include <linux/irq.h>20#include <linux/interrupt.h>21#include <linux/io.h>22#include <linux/of_address.h>23#include <linux/of_irq.h>24#include <asm/time.h>25#include <asm/machdep.h>26#include <asm/mpc52xx.h>2728static const struct of_device_id mpc5200_gpio_ids[] __initconst = {29{ .compatible = "fsl,mpc5200-gpio", },30{ .compatible = "mpc5200-gpio", },31{}32};3334/* FPGA register set */35#define MEDIA5200_IRQ_ENABLE (0x40c)36#define MEDIA5200_IRQ_STATUS (0x410)37#define MEDIA5200_NUM_IRQS (6)38#define MEDIA5200_IRQ_SHIFT (32 - MEDIA5200_NUM_IRQS)3940struct media5200_irq {41void __iomem *regs;42spinlock_t lock;43struct irq_domain *irqhost;44};45struct media5200_irq media5200_irq;4647static void media5200_irq_unmask(struct irq_data *d)48{49unsigned long flags;50u32 val;5152spin_lock_irqsave(&media5200_irq.lock, flags);53val = in_be32(media5200_irq.regs + MEDIA5200_IRQ_ENABLE);54val |= 1 << (MEDIA5200_IRQ_SHIFT + irqd_to_hwirq(d));55out_be32(media5200_irq.regs + MEDIA5200_IRQ_ENABLE, val);56spin_unlock_irqrestore(&media5200_irq.lock, flags);57}5859static void media5200_irq_mask(struct irq_data *d)60{61unsigned long flags;62u32 val;6364spin_lock_irqsave(&media5200_irq.lock, flags);65val = in_be32(media5200_irq.regs + MEDIA5200_IRQ_ENABLE);66val &= ~(1 << (MEDIA5200_IRQ_SHIFT + irqd_to_hwirq(d)));67out_be32(media5200_irq.regs + MEDIA5200_IRQ_ENABLE, val);68spin_unlock_irqrestore(&media5200_irq.lock, flags);69}7071static struct irq_chip media5200_irq_chip = {72.name = "Media5200 FPGA",73.irq_unmask = media5200_irq_unmask,74.irq_mask = media5200_irq_mask,75.irq_mask_ack = media5200_irq_mask,76};7778static void media5200_irq_cascade(struct irq_desc *desc)79{80struct irq_chip *chip = irq_desc_get_chip(desc);81int val;82u32 status, enable;8384/* Mask off the cascaded IRQ */85raw_spin_lock(&desc->lock);86chip->irq_mask(&desc->irq_data);87raw_spin_unlock(&desc->lock);8889/* Ask the FPGA for IRQ status. If 'val' is 0, then no irqs90* are pending. 'ffs()' is 1 based */91status = in_be32(media5200_irq.regs + MEDIA5200_IRQ_ENABLE);92enable = in_be32(media5200_irq.regs + MEDIA5200_IRQ_STATUS);93val = ffs((status & enable) >> MEDIA5200_IRQ_SHIFT);94if (val) {95generic_handle_domain_irq(media5200_irq.irqhost, val - 1);96/* pr_debug("%s: virq=%i s=%.8x e=%.8x hwirq=%i\n",97* __func__, virq, status, enable, val - 1);98*/99}100101/* Processing done; can reenable the cascade now */102raw_spin_lock(&desc->lock);103chip->irq_ack(&desc->irq_data);104if (!irqd_irq_disabled(&desc->irq_data))105chip->irq_unmask(&desc->irq_data);106raw_spin_unlock(&desc->lock);107}108109static int media5200_irq_map(struct irq_domain *h, unsigned int virq,110irq_hw_number_t hw)111{112pr_debug("%s: h=%p, virq=%i, hwirq=%i\n", __func__, h, virq, (int)hw);113irq_set_chip_data(virq, &media5200_irq);114irq_set_chip_and_handler(virq, &media5200_irq_chip, handle_level_irq);115irq_set_status_flags(virq, IRQ_LEVEL);116return 0;117}118119static int media5200_irq_xlate(struct irq_domain *h, struct device_node *ct,120const u32 *intspec, unsigned int intsize,121irq_hw_number_t *out_hwirq,122unsigned int *out_flags)123{124if (intsize != 2)125return -1;126127pr_debug("%s: bank=%i, number=%i\n", __func__, intspec[0], intspec[1]);128*out_hwirq = intspec[1];129*out_flags = IRQ_TYPE_NONE;130return 0;131}132133static const struct irq_domain_ops media5200_irq_ops = {134.map = media5200_irq_map,135.xlate = media5200_irq_xlate,136};137138/*139* Setup Media5200 IRQ mapping140*/141static void __init media5200_init_irq(void)142{143struct device_node *fpga_np;144int cascade_virq;145146/* First setup the regular MPC5200 interrupt controller */147mpc52xx_init_irq();148149/* Now find the FPGA IRQ */150fpga_np = of_find_compatible_node(NULL, NULL, "fsl,media5200-fpga");151if (!fpga_np)152goto out;153pr_debug("%s: found fpga node: %pOF\n", __func__, fpga_np);154155media5200_irq.regs = of_iomap(fpga_np, 0);156if (!media5200_irq.regs)157goto out;158pr_debug("%s: mapped to %p\n", __func__, media5200_irq.regs);159160cascade_virq = irq_of_parse_and_map(fpga_np, 0);161if (!cascade_virq)162goto out;163pr_debug("%s: cascaded on virq=%i\n", __func__, cascade_virq);164165/* Disable all FPGA IRQs */166out_be32(media5200_irq.regs + MEDIA5200_IRQ_ENABLE, 0);167168spin_lock_init(&media5200_irq.lock);169170media5200_irq.irqhost = irq_domain_create_linear(of_fwnode_handle(fpga_np),171MEDIA5200_NUM_IRQS, &media5200_irq_ops, &media5200_irq);172if (!media5200_irq.irqhost)173goto out;174pr_debug("%s: allocated irqhost\n", __func__);175176of_node_put(fpga_np);177178irq_set_handler_data(cascade_virq, &media5200_irq);179irq_set_chained_handler(cascade_virq, media5200_irq_cascade);180181return;182183out:184pr_err("Could not find Media5200 FPGA; PCI interrupts will not work\n");185of_node_put(fpga_np);186}187188/*189* Setup the architecture190*/191static void __init media5200_setup_arch(void)192{193194struct device_node *np;195struct mpc52xx_gpio __iomem *gpio;196u32 port_config;197198if (ppc_md.progress)199ppc_md.progress("media5200_setup_arch()", 0);200201/* Map important registers from the internal memory map */202mpc52xx_map_common_devices();203204/* Some mpc5200 & mpc5200b related configuration */205mpc5200_setup_xlb_arbiter();206207np = of_find_matching_node(NULL, mpc5200_gpio_ids);208gpio = of_iomap(np, 0);209of_node_put(np);210if (!gpio) {211printk(KERN_ERR "%s() failed. expect abnormal behavior\n",212__func__);213return;214}215216/* Set port config */217port_config = in_be32(&gpio->port_config);218219port_config &= ~0x03000000; /* ATA CS is on csb_4/5 */220port_config |= 0x01000000;221222out_be32(&gpio->port_config, port_config);223224/* Unmap zone */225iounmap(gpio);226227}228229define_machine(media5200_platform) {230.name = "media5200-platform",231.compatible = "fsl,media5200",232.setup_arch = media5200_setup_arch,233.discover_phbs = mpc52xx_setup_pci,234.init = mpc52xx_declare_of_platform_devices,235.init_IRQ = media5200_init_irq,236.get_irq = mpc52xx_get_irq,237.restart = mpc52xx_restart,238};239240241