18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Support for 'media5200-platform' compatible boards. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2008 Secret Lab Technologies Ltd. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Description: 88c2ecf20Sopenharmony_ci * This code implements support for the Freescape Media5200 platform 98c2ecf20Sopenharmony_ci * (built around the MPC5200 SoC). 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * Notable characteristic of the Media5200 is the presence of an FPGA 128c2ecf20Sopenharmony_ci * that has all external IRQ lines routed through it. This file implements 138c2ecf20Sopenharmony_ci * a cascaded interrupt controller driver which attaches itself to the 148c2ecf20Sopenharmony_ci * Virtual IRQ subsystem after the primary mpc5200 interrupt controller 158c2ecf20Sopenharmony_ci * is initialized. 168c2ecf20Sopenharmony_ci */ 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#undef DEBUG 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <linux/irq.h> 218c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 228c2ecf20Sopenharmony_ci#include <linux/io.h> 238c2ecf20Sopenharmony_ci#include <asm/time.h> 248c2ecf20Sopenharmony_ci#include <asm/prom.h> 258c2ecf20Sopenharmony_ci#include <asm/machdep.h> 268c2ecf20Sopenharmony_ci#include <asm/mpc52xx.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic const struct of_device_id mpc5200_gpio_ids[] __initconst = { 298c2ecf20Sopenharmony_ci { .compatible = "fsl,mpc5200-gpio", }, 308c2ecf20Sopenharmony_ci { .compatible = "mpc5200-gpio", }, 318c2ecf20Sopenharmony_ci {} 328c2ecf20Sopenharmony_ci}; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci/* FPGA register set */ 358c2ecf20Sopenharmony_ci#define MEDIA5200_IRQ_ENABLE (0x40c) 368c2ecf20Sopenharmony_ci#define MEDIA5200_IRQ_STATUS (0x410) 378c2ecf20Sopenharmony_ci#define MEDIA5200_NUM_IRQS (6) 388c2ecf20Sopenharmony_ci#define MEDIA5200_IRQ_SHIFT (32 - MEDIA5200_NUM_IRQS) 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistruct media5200_irq { 418c2ecf20Sopenharmony_ci void __iomem *regs; 428c2ecf20Sopenharmony_ci spinlock_t lock; 438c2ecf20Sopenharmony_ci struct irq_domain *irqhost; 448c2ecf20Sopenharmony_ci}; 458c2ecf20Sopenharmony_cistruct media5200_irq media5200_irq; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic void media5200_irq_unmask(struct irq_data *d) 488c2ecf20Sopenharmony_ci{ 498c2ecf20Sopenharmony_ci unsigned long flags; 508c2ecf20Sopenharmony_ci u32 val; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci spin_lock_irqsave(&media5200_irq.lock, flags); 538c2ecf20Sopenharmony_ci val = in_be32(media5200_irq.regs + MEDIA5200_IRQ_ENABLE); 548c2ecf20Sopenharmony_ci val |= 1 << (MEDIA5200_IRQ_SHIFT + irqd_to_hwirq(d)); 558c2ecf20Sopenharmony_ci out_be32(media5200_irq.regs + MEDIA5200_IRQ_ENABLE, val); 568c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&media5200_irq.lock, flags); 578c2ecf20Sopenharmony_ci} 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic void media5200_irq_mask(struct irq_data *d) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci unsigned long flags; 628c2ecf20Sopenharmony_ci u32 val; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci spin_lock_irqsave(&media5200_irq.lock, flags); 658c2ecf20Sopenharmony_ci val = in_be32(media5200_irq.regs + MEDIA5200_IRQ_ENABLE); 668c2ecf20Sopenharmony_ci val &= ~(1 << (MEDIA5200_IRQ_SHIFT + irqd_to_hwirq(d))); 678c2ecf20Sopenharmony_ci out_be32(media5200_irq.regs + MEDIA5200_IRQ_ENABLE, val); 688c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&media5200_irq.lock, flags); 698c2ecf20Sopenharmony_ci} 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic struct irq_chip media5200_irq_chip = { 728c2ecf20Sopenharmony_ci .name = "Media5200 FPGA", 738c2ecf20Sopenharmony_ci .irq_unmask = media5200_irq_unmask, 748c2ecf20Sopenharmony_ci .irq_mask = media5200_irq_mask, 758c2ecf20Sopenharmony_ci .irq_mask_ack = media5200_irq_mask, 768c2ecf20Sopenharmony_ci}; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic void media5200_irq_cascade(struct irq_desc *desc) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci struct irq_chip *chip = irq_desc_get_chip(desc); 818c2ecf20Sopenharmony_ci int sub_virq, val; 828c2ecf20Sopenharmony_ci u32 status, enable; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci /* Mask off the cascaded IRQ */ 858c2ecf20Sopenharmony_ci raw_spin_lock(&desc->lock); 868c2ecf20Sopenharmony_ci chip->irq_mask(&desc->irq_data); 878c2ecf20Sopenharmony_ci raw_spin_unlock(&desc->lock); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci /* Ask the FPGA for IRQ status. If 'val' is 0, then no irqs 908c2ecf20Sopenharmony_ci * are pending. 'ffs()' is 1 based */ 918c2ecf20Sopenharmony_ci status = in_be32(media5200_irq.regs + MEDIA5200_IRQ_ENABLE); 928c2ecf20Sopenharmony_ci enable = in_be32(media5200_irq.regs + MEDIA5200_IRQ_STATUS); 938c2ecf20Sopenharmony_ci val = ffs((status & enable) >> MEDIA5200_IRQ_SHIFT); 948c2ecf20Sopenharmony_ci if (val) { 958c2ecf20Sopenharmony_ci sub_virq = irq_linear_revmap(media5200_irq.irqhost, val - 1); 968c2ecf20Sopenharmony_ci /* pr_debug("%s: virq=%i s=%.8x e=%.8x hwirq=%i subvirq=%i\n", 978c2ecf20Sopenharmony_ci * __func__, virq, status, enable, val - 1, sub_virq); 988c2ecf20Sopenharmony_ci */ 998c2ecf20Sopenharmony_ci generic_handle_irq(sub_virq); 1008c2ecf20Sopenharmony_ci } 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci /* Processing done; can reenable the cascade now */ 1038c2ecf20Sopenharmony_ci raw_spin_lock(&desc->lock); 1048c2ecf20Sopenharmony_ci chip->irq_ack(&desc->irq_data); 1058c2ecf20Sopenharmony_ci if (!irqd_irq_disabled(&desc->irq_data)) 1068c2ecf20Sopenharmony_ci chip->irq_unmask(&desc->irq_data); 1078c2ecf20Sopenharmony_ci raw_spin_unlock(&desc->lock); 1088c2ecf20Sopenharmony_ci} 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_cistatic int media5200_irq_map(struct irq_domain *h, unsigned int virq, 1118c2ecf20Sopenharmony_ci irq_hw_number_t hw) 1128c2ecf20Sopenharmony_ci{ 1138c2ecf20Sopenharmony_ci pr_debug("%s: h=%p, virq=%i, hwirq=%i\n", __func__, h, virq, (int)hw); 1148c2ecf20Sopenharmony_ci irq_set_chip_data(virq, &media5200_irq); 1158c2ecf20Sopenharmony_ci irq_set_chip_and_handler(virq, &media5200_irq_chip, handle_level_irq); 1168c2ecf20Sopenharmony_ci irq_set_status_flags(virq, IRQ_LEVEL); 1178c2ecf20Sopenharmony_ci return 0; 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic int media5200_irq_xlate(struct irq_domain *h, struct device_node *ct, 1218c2ecf20Sopenharmony_ci const u32 *intspec, unsigned int intsize, 1228c2ecf20Sopenharmony_ci irq_hw_number_t *out_hwirq, 1238c2ecf20Sopenharmony_ci unsigned int *out_flags) 1248c2ecf20Sopenharmony_ci{ 1258c2ecf20Sopenharmony_ci if (intsize != 2) 1268c2ecf20Sopenharmony_ci return -1; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci pr_debug("%s: bank=%i, number=%i\n", __func__, intspec[0], intspec[1]); 1298c2ecf20Sopenharmony_ci *out_hwirq = intspec[1]; 1308c2ecf20Sopenharmony_ci *out_flags = IRQ_TYPE_NONE; 1318c2ecf20Sopenharmony_ci return 0; 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic const struct irq_domain_ops media5200_irq_ops = { 1358c2ecf20Sopenharmony_ci .map = media5200_irq_map, 1368c2ecf20Sopenharmony_ci .xlate = media5200_irq_xlate, 1378c2ecf20Sopenharmony_ci}; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci/* 1408c2ecf20Sopenharmony_ci * Setup Media5200 IRQ mapping 1418c2ecf20Sopenharmony_ci */ 1428c2ecf20Sopenharmony_cistatic void __init media5200_init_irq(void) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci struct device_node *fpga_np; 1458c2ecf20Sopenharmony_ci int cascade_virq; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci /* First setup the regular MPC5200 interrupt controller */ 1488c2ecf20Sopenharmony_ci mpc52xx_init_irq(); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci /* Now find the FPGA IRQ */ 1518c2ecf20Sopenharmony_ci fpga_np = of_find_compatible_node(NULL, NULL, "fsl,media5200-fpga"); 1528c2ecf20Sopenharmony_ci if (!fpga_np) 1538c2ecf20Sopenharmony_ci goto out; 1548c2ecf20Sopenharmony_ci pr_debug("%s: found fpga node: %pOF\n", __func__, fpga_np); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci media5200_irq.regs = of_iomap(fpga_np, 0); 1578c2ecf20Sopenharmony_ci if (!media5200_irq.regs) 1588c2ecf20Sopenharmony_ci goto out; 1598c2ecf20Sopenharmony_ci pr_debug("%s: mapped to %p\n", __func__, media5200_irq.regs); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci cascade_virq = irq_of_parse_and_map(fpga_np, 0); 1628c2ecf20Sopenharmony_ci if (!cascade_virq) 1638c2ecf20Sopenharmony_ci goto out; 1648c2ecf20Sopenharmony_ci pr_debug("%s: cascaded on virq=%i\n", __func__, cascade_virq); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci /* Disable all FPGA IRQs */ 1678c2ecf20Sopenharmony_ci out_be32(media5200_irq.regs + MEDIA5200_IRQ_ENABLE, 0); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci spin_lock_init(&media5200_irq.lock); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci media5200_irq.irqhost = irq_domain_add_linear(fpga_np, 1728c2ecf20Sopenharmony_ci MEDIA5200_NUM_IRQS, &media5200_irq_ops, &media5200_irq); 1738c2ecf20Sopenharmony_ci if (!media5200_irq.irqhost) 1748c2ecf20Sopenharmony_ci goto out; 1758c2ecf20Sopenharmony_ci pr_debug("%s: allocated irqhost\n", __func__); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci irq_set_handler_data(cascade_virq, &media5200_irq); 1788c2ecf20Sopenharmony_ci irq_set_chained_handler(cascade_virq, media5200_irq_cascade); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci return; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci out: 1838c2ecf20Sopenharmony_ci pr_err("Could not find Media5200 FPGA; PCI interrupts will not work\n"); 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci/* 1878c2ecf20Sopenharmony_ci * Setup the architecture 1888c2ecf20Sopenharmony_ci */ 1898c2ecf20Sopenharmony_cistatic void __init media5200_setup_arch(void) 1908c2ecf20Sopenharmony_ci{ 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci struct device_node *np; 1938c2ecf20Sopenharmony_ci struct mpc52xx_gpio __iomem *gpio; 1948c2ecf20Sopenharmony_ci u32 port_config; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci if (ppc_md.progress) 1978c2ecf20Sopenharmony_ci ppc_md.progress("media5200_setup_arch()", 0); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci /* Map important registers from the internal memory map */ 2008c2ecf20Sopenharmony_ci mpc52xx_map_common_devices(); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci /* Some mpc5200 & mpc5200b related configuration */ 2038c2ecf20Sopenharmony_ci mpc5200_setup_xlb_arbiter(); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci mpc52xx_setup_pci(); 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci np = of_find_matching_node(NULL, mpc5200_gpio_ids); 2088c2ecf20Sopenharmony_ci gpio = of_iomap(np, 0); 2098c2ecf20Sopenharmony_ci of_node_put(np); 2108c2ecf20Sopenharmony_ci if (!gpio) { 2118c2ecf20Sopenharmony_ci printk(KERN_ERR "%s() failed. expect abnormal behavior\n", 2128c2ecf20Sopenharmony_ci __func__); 2138c2ecf20Sopenharmony_ci return; 2148c2ecf20Sopenharmony_ci } 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci /* Set port config */ 2178c2ecf20Sopenharmony_ci port_config = in_be32(&gpio->port_config); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci port_config &= ~0x03000000; /* ATA CS is on csb_4/5 */ 2208c2ecf20Sopenharmony_ci port_config |= 0x01000000; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci out_be32(&gpio->port_config, port_config); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci /* Unmap zone */ 2258c2ecf20Sopenharmony_ci iounmap(gpio); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci/* list of the supported boards */ 2308c2ecf20Sopenharmony_cistatic const char * const board[] __initconst = { 2318c2ecf20Sopenharmony_ci "fsl,media5200", 2328c2ecf20Sopenharmony_ci NULL 2338c2ecf20Sopenharmony_ci}; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci/* 2368c2ecf20Sopenharmony_ci * Called very early, MMU is off, device-tree isn't unflattened 2378c2ecf20Sopenharmony_ci */ 2388c2ecf20Sopenharmony_cistatic int __init media5200_probe(void) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci return of_device_compatible_match(of_root, board); 2418c2ecf20Sopenharmony_ci} 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_cidefine_machine(media5200_platform) { 2448c2ecf20Sopenharmony_ci .name = "media5200-platform", 2458c2ecf20Sopenharmony_ci .probe = media5200_probe, 2468c2ecf20Sopenharmony_ci .setup_arch = media5200_setup_arch, 2478c2ecf20Sopenharmony_ci .init = mpc52xx_declare_of_platform_devices, 2488c2ecf20Sopenharmony_ci .init_IRQ = media5200_init_irq, 2498c2ecf20Sopenharmony_ci .get_irq = mpc52xx_get_irq, 2508c2ecf20Sopenharmony_ci .restart = mpc52xx_restart, 2518c2ecf20Sopenharmony_ci .calibrate_decr = generic_calibrate_decr, 2528c2ecf20Sopenharmony_ci}; 253