18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright 2007, Michael Ellerman, IBM Corporation. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 88c2ecf20Sopenharmony_ci#include <linux/irq.h> 98c2ecf20Sopenharmony_ci#include <linux/kernel.h> 108c2ecf20Sopenharmony_ci#include <linux/pci.h> 118c2ecf20Sopenharmony_ci#include <linux/msi.h> 128c2ecf20Sopenharmony_ci#include <linux/export.h> 138c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 148c2ecf20Sopenharmony_ci#include <linux/slab.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <asm/debugfs.h> 178c2ecf20Sopenharmony_ci#include <asm/dcr.h> 188c2ecf20Sopenharmony_ci#include <asm/machdep.h> 198c2ecf20Sopenharmony_ci#include <asm/prom.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include "cell.h" 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci/* 248c2ecf20Sopenharmony_ci * MSIC registers, specified as offsets from dcr_base 258c2ecf20Sopenharmony_ci */ 268c2ecf20Sopenharmony_ci#define MSIC_CTRL_REG 0x0 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/* Base Address registers specify FIFO location in BE memory */ 298c2ecf20Sopenharmony_ci#define MSIC_BASE_ADDR_HI_REG 0x3 308c2ecf20Sopenharmony_ci#define MSIC_BASE_ADDR_LO_REG 0x4 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci/* Hold the read/write offsets into the FIFO */ 338c2ecf20Sopenharmony_ci#define MSIC_READ_OFFSET_REG 0x5 348c2ecf20Sopenharmony_ci#define MSIC_WRITE_OFFSET_REG 0x6 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci/* MSIC control register flags */ 388c2ecf20Sopenharmony_ci#define MSIC_CTRL_ENABLE 0x0001 398c2ecf20Sopenharmony_ci#define MSIC_CTRL_FIFO_FULL_ENABLE 0x0002 408c2ecf20Sopenharmony_ci#define MSIC_CTRL_IRQ_ENABLE 0x0008 418c2ecf20Sopenharmony_ci#define MSIC_CTRL_FULL_STOP_ENABLE 0x0010 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci/* 448c2ecf20Sopenharmony_ci * The MSIC can be configured to use a FIFO of 32KB, 64KB, 128KB or 256KB. 458c2ecf20Sopenharmony_ci * Currently we're using a 64KB FIFO size. 468c2ecf20Sopenharmony_ci */ 478c2ecf20Sopenharmony_ci#define MSIC_FIFO_SIZE_SHIFT 16 488c2ecf20Sopenharmony_ci#define MSIC_FIFO_SIZE_BYTES (1 << MSIC_FIFO_SIZE_SHIFT) 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci/* 518c2ecf20Sopenharmony_ci * To configure the FIFO size as (1 << n) bytes, we write (n - 15) into bits 528c2ecf20Sopenharmony_ci * 8-9 of the MSIC control reg. 538c2ecf20Sopenharmony_ci */ 548c2ecf20Sopenharmony_ci#define MSIC_CTRL_FIFO_SIZE (((MSIC_FIFO_SIZE_SHIFT - 15) << 8) & 0x300) 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci/* 578c2ecf20Sopenharmony_ci * We need to mask the read/write offsets to make sure they stay within 588c2ecf20Sopenharmony_ci * the bounds of the FIFO. Also they should always be 16-byte aligned. 598c2ecf20Sopenharmony_ci */ 608c2ecf20Sopenharmony_ci#define MSIC_FIFO_SIZE_MASK ((MSIC_FIFO_SIZE_BYTES - 1) & ~0xFu) 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci/* Each entry in the FIFO is 16 bytes, the first 4 bytes hold the irq # */ 638c2ecf20Sopenharmony_ci#define MSIC_FIFO_ENTRY_SIZE 0x10 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistruct axon_msic { 678c2ecf20Sopenharmony_ci struct irq_domain *irq_domain; 688c2ecf20Sopenharmony_ci __le32 *fifo_virt; 698c2ecf20Sopenharmony_ci dma_addr_t fifo_phys; 708c2ecf20Sopenharmony_ci dcr_host_t dcr_host; 718c2ecf20Sopenharmony_ci u32 read_offset; 728c2ecf20Sopenharmony_ci#ifdef DEBUG 738c2ecf20Sopenharmony_ci u32 __iomem *trigger; 748c2ecf20Sopenharmony_ci#endif 758c2ecf20Sopenharmony_ci}; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci#ifdef DEBUG 788c2ecf20Sopenharmony_civoid axon_msi_debug_setup(struct device_node *dn, struct axon_msic *msic); 798c2ecf20Sopenharmony_ci#else 808c2ecf20Sopenharmony_cistatic inline void axon_msi_debug_setup(struct device_node *dn, 818c2ecf20Sopenharmony_ci struct axon_msic *msic) { } 828c2ecf20Sopenharmony_ci#endif 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cistatic void msic_dcr_write(struct axon_msic *msic, unsigned int dcr_n, u32 val) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci pr_devel("axon_msi: dcr_write(0x%x, 0x%x)\n", val, dcr_n); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci dcr_write(msic->dcr_host, dcr_n, val); 908c2ecf20Sopenharmony_ci} 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistatic void axon_msi_cascade(struct irq_desc *desc) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci struct irq_chip *chip = irq_desc_get_chip(desc); 958c2ecf20Sopenharmony_ci struct axon_msic *msic = irq_desc_get_handler_data(desc); 968c2ecf20Sopenharmony_ci u32 write_offset, msi; 978c2ecf20Sopenharmony_ci int idx; 988c2ecf20Sopenharmony_ci int retry = 0; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci write_offset = dcr_read(msic->dcr_host, MSIC_WRITE_OFFSET_REG); 1018c2ecf20Sopenharmony_ci pr_devel("axon_msi: original write_offset 0x%x\n", write_offset); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci /* write_offset doesn't wrap properly, so we have to mask it */ 1048c2ecf20Sopenharmony_ci write_offset &= MSIC_FIFO_SIZE_MASK; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci while (msic->read_offset != write_offset && retry < 100) { 1078c2ecf20Sopenharmony_ci idx = msic->read_offset / sizeof(__le32); 1088c2ecf20Sopenharmony_ci msi = le32_to_cpu(msic->fifo_virt[idx]); 1098c2ecf20Sopenharmony_ci msi &= 0xFFFF; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci pr_devel("axon_msi: woff %x roff %x msi %x\n", 1128c2ecf20Sopenharmony_ci write_offset, msic->read_offset, msi); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci if (msi < nr_irqs && irq_get_chip_data(msi) == msic) { 1158c2ecf20Sopenharmony_ci generic_handle_irq(msi); 1168c2ecf20Sopenharmony_ci msic->fifo_virt[idx] = cpu_to_le32(0xffffffff); 1178c2ecf20Sopenharmony_ci } else { 1188c2ecf20Sopenharmony_ci /* 1198c2ecf20Sopenharmony_ci * Reading the MSIC_WRITE_OFFSET_REG does not 1208c2ecf20Sopenharmony_ci * reliably flush the outstanding DMA to the 1218c2ecf20Sopenharmony_ci * FIFO buffer. Here we were reading stale 1228c2ecf20Sopenharmony_ci * data, so we need to retry. 1238c2ecf20Sopenharmony_ci */ 1248c2ecf20Sopenharmony_ci udelay(1); 1258c2ecf20Sopenharmony_ci retry++; 1268c2ecf20Sopenharmony_ci pr_devel("axon_msi: invalid irq 0x%x!\n", msi); 1278c2ecf20Sopenharmony_ci continue; 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci if (retry) { 1318c2ecf20Sopenharmony_ci pr_devel("axon_msi: late irq 0x%x, retry %d\n", 1328c2ecf20Sopenharmony_ci msi, retry); 1338c2ecf20Sopenharmony_ci retry = 0; 1348c2ecf20Sopenharmony_ci } 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci msic->read_offset += MSIC_FIFO_ENTRY_SIZE; 1378c2ecf20Sopenharmony_ci msic->read_offset &= MSIC_FIFO_SIZE_MASK; 1388c2ecf20Sopenharmony_ci } 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci if (retry) { 1418c2ecf20Sopenharmony_ci printk(KERN_WARNING "axon_msi: irq timed out\n"); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci msic->read_offset += MSIC_FIFO_ENTRY_SIZE; 1448c2ecf20Sopenharmony_ci msic->read_offset &= MSIC_FIFO_SIZE_MASK; 1458c2ecf20Sopenharmony_ci } 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci chip->irq_eoi(&desc->irq_data); 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic struct axon_msic *find_msi_translator(struct pci_dev *dev) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci struct irq_domain *irq_domain; 1538c2ecf20Sopenharmony_ci struct device_node *dn, *tmp; 1548c2ecf20Sopenharmony_ci const phandle *ph; 1558c2ecf20Sopenharmony_ci struct axon_msic *msic = NULL; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci dn = of_node_get(pci_device_to_OF_node(dev)); 1588c2ecf20Sopenharmony_ci if (!dn) { 1598c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "axon_msi: no pci_dn found\n"); 1608c2ecf20Sopenharmony_ci return NULL; 1618c2ecf20Sopenharmony_ci } 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci for (; dn; dn = of_get_next_parent(dn)) { 1648c2ecf20Sopenharmony_ci ph = of_get_property(dn, "msi-translator", NULL); 1658c2ecf20Sopenharmony_ci if (ph) 1668c2ecf20Sopenharmony_ci break; 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci if (!ph) { 1708c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, 1718c2ecf20Sopenharmony_ci "axon_msi: no msi-translator property found\n"); 1728c2ecf20Sopenharmony_ci goto out_error; 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci tmp = dn; 1768c2ecf20Sopenharmony_ci dn = of_find_node_by_phandle(*ph); 1778c2ecf20Sopenharmony_ci of_node_put(tmp); 1788c2ecf20Sopenharmony_ci if (!dn) { 1798c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, 1808c2ecf20Sopenharmony_ci "axon_msi: msi-translator doesn't point to a node\n"); 1818c2ecf20Sopenharmony_ci goto out_error; 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci irq_domain = irq_find_host(dn); 1858c2ecf20Sopenharmony_ci if (!irq_domain) { 1868c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "axon_msi: no irq_domain found for node %pOF\n", 1878c2ecf20Sopenharmony_ci dn); 1888c2ecf20Sopenharmony_ci goto out_error; 1898c2ecf20Sopenharmony_ci } 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci msic = irq_domain->host_data; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ciout_error: 1948c2ecf20Sopenharmony_ci of_node_put(dn); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci return msic; 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_cistatic int setup_msi_msg_address(struct pci_dev *dev, struct msi_msg *msg) 2008c2ecf20Sopenharmony_ci{ 2018c2ecf20Sopenharmony_ci struct device_node *dn; 2028c2ecf20Sopenharmony_ci struct msi_desc *entry; 2038c2ecf20Sopenharmony_ci int len; 2048c2ecf20Sopenharmony_ci const u32 *prop; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci dn = of_node_get(pci_device_to_OF_node(dev)); 2078c2ecf20Sopenharmony_ci if (!dn) { 2088c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "axon_msi: no pci_dn found\n"); 2098c2ecf20Sopenharmony_ci return -ENODEV; 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci entry = first_pci_msi_entry(dev); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci for (; dn; dn = of_get_next_parent(dn)) { 2158c2ecf20Sopenharmony_ci if (entry->msi_attrib.is_64) { 2168c2ecf20Sopenharmony_ci prop = of_get_property(dn, "msi-address-64", &len); 2178c2ecf20Sopenharmony_ci if (prop) 2188c2ecf20Sopenharmony_ci break; 2198c2ecf20Sopenharmony_ci } 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci prop = of_get_property(dn, "msi-address-32", &len); 2228c2ecf20Sopenharmony_ci if (prop) 2238c2ecf20Sopenharmony_ci break; 2248c2ecf20Sopenharmony_ci } 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci if (!prop) { 2278c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, 2288c2ecf20Sopenharmony_ci "axon_msi: no msi-address-(32|64) properties found\n"); 2298c2ecf20Sopenharmony_ci of_node_put(dn); 2308c2ecf20Sopenharmony_ci return -ENOENT; 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci switch (len) { 2348c2ecf20Sopenharmony_ci case 8: 2358c2ecf20Sopenharmony_ci msg->address_hi = prop[0]; 2368c2ecf20Sopenharmony_ci msg->address_lo = prop[1]; 2378c2ecf20Sopenharmony_ci break; 2388c2ecf20Sopenharmony_ci case 4: 2398c2ecf20Sopenharmony_ci msg->address_hi = 0; 2408c2ecf20Sopenharmony_ci msg->address_lo = prop[0]; 2418c2ecf20Sopenharmony_ci break; 2428c2ecf20Sopenharmony_ci default: 2438c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, 2448c2ecf20Sopenharmony_ci "axon_msi: malformed msi-address-(32|64) property\n"); 2458c2ecf20Sopenharmony_ci of_node_put(dn); 2468c2ecf20Sopenharmony_ci return -EINVAL; 2478c2ecf20Sopenharmony_ci } 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci of_node_put(dn); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci return 0; 2528c2ecf20Sopenharmony_ci} 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_cistatic int axon_msi_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) 2558c2ecf20Sopenharmony_ci{ 2568c2ecf20Sopenharmony_ci unsigned int virq, rc; 2578c2ecf20Sopenharmony_ci struct msi_desc *entry; 2588c2ecf20Sopenharmony_ci struct msi_msg msg; 2598c2ecf20Sopenharmony_ci struct axon_msic *msic; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci msic = find_msi_translator(dev); 2628c2ecf20Sopenharmony_ci if (!msic) 2638c2ecf20Sopenharmony_ci return -ENODEV; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci rc = setup_msi_msg_address(dev, &msg); 2668c2ecf20Sopenharmony_ci if (rc) 2678c2ecf20Sopenharmony_ci return rc; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci for_each_pci_msi_entry(entry, dev) { 2708c2ecf20Sopenharmony_ci virq = irq_create_direct_mapping(msic->irq_domain); 2718c2ecf20Sopenharmony_ci if (!virq) { 2728c2ecf20Sopenharmony_ci dev_warn(&dev->dev, 2738c2ecf20Sopenharmony_ci "axon_msi: virq allocation failed!\n"); 2748c2ecf20Sopenharmony_ci return -1; 2758c2ecf20Sopenharmony_ci } 2768c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "axon_msi: allocated virq 0x%x\n", virq); 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci irq_set_msi_desc(virq, entry); 2798c2ecf20Sopenharmony_ci msg.data = virq; 2808c2ecf20Sopenharmony_ci pci_write_msi_msg(virq, &msg); 2818c2ecf20Sopenharmony_ci } 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci return 0; 2848c2ecf20Sopenharmony_ci} 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_cistatic void axon_msi_teardown_msi_irqs(struct pci_dev *dev) 2878c2ecf20Sopenharmony_ci{ 2888c2ecf20Sopenharmony_ci struct msi_desc *entry; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "axon_msi: tearing down msi irqs\n"); 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci for_each_pci_msi_entry(entry, dev) { 2938c2ecf20Sopenharmony_ci if (!entry->irq) 2948c2ecf20Sopenharmony_ci continue; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci irq_set_msi_desc(entry->irq, NULL); 2978c2ecf20Sopenharmony_ci irq_dispose_mapping(entry->irq); 2988c2ecf20Sopenharmony_ci } 2998c2ecf20Sopenharmony_ci} 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_cistatic struct irq_chip msic_irq_chip = { 3028c2ecf20Sopenharmony_ci .irq_mask = pci_msi_mask_irq, 3038c2ecf20Sopenharmony_ci .irq_unmask = pci_msi_unmask_irq, 3048c2ecf20Sopenharmony_ci .irq_shutdown = pci_msi_mask_irq, 3058c2ecf20Sopenharmony_ci .name = "AXON-MSI", 3068c2ecf20Sopenharmony_ci}; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_cistatic int msic_host_map(struct irq_domain *h, unsigned int virq, 3098c2ecf20Sopenharmony_ci irq_hw_number_t hw) 3108c2ecf20Sopenharmony_ci{ 3118c2ecf20Sopenharmony_ci irq_set_chip_data(virq, h->host_data); 3128c2ecf20Sopenharmony_ci irq_set_chip_and_handler(virq, &msic_irq_chip, handle_simple_irq); 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci return 0; 3158c2ecf20Sopenharmony_ci} 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_cistatic const struct irq_domain_ops msic_host_ops = { 3188c2ecf20Sopenharmony_ci .map = msic_host_map, 3198c2ecf20Sopenharmony_ci}; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_cistatic void axon_msi_shutdown(struct platform_device *device) 3228c2ecf20Sopenharmony_ci{ 3238c2ecf20Sopenharmony_ci struct axon_msic *msic = dev_get_drvdata(&device->dev); 3248c2ecf20Sopenharmony_ci u32 tmp; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci pr_devel("axon_msi: disabling %pOF\n", 3278c2ecf20Sopenharmony_ci irq_domain_get_of_node(msic->irq_domain)); 3288c2ecf20Sopenharmony_ci tmp = dcr_read(msic->dcr_host, MSIC_CTRL_REG); 3298c2ecf20Sopenharmony_ci tmp &= ~MSIC_CTRL_ENABLE & ~MSIC_CTRL_IRQ_ENABLE; 3308c2ecf20Sopenharmony_ci msic_dcr_write(msic, MSIC_CTRL_REG, tmp); 3318c2ecf20Sopenharmony_ci} 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_cistatic int axon_msi_probe(struct platform_device *device) 3348c2ecf20Sopenharmony_ci{ 3358c2ecf20Sopenharmony_ci struct device_node *dn = device->dev.of_node; 3368c2ecf20Sopenharmony_ci struct axon_msic *msic; 3378c2ecf20Sopenharmony_ci unsigned int virq; 3388c2ecf20Sopenharmony_ci int dcr_base, dcr_len; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci pr_devel("axon_msi: setting up dn %pOF\n", dn); 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci msic = kzalloc(sizeof(*msic), GFP_KERNEL); 3438c2ecf20Sopenharmony_ci if (!msic) { 3448c2ecf20Sopenharmony_ci printk(KERN_ERR "axon_msi: couldn't allocate msic for %pOF\n", 3458c2ecf20Sopenharmony_ci dn); 3468c2ecf20Sopenharmony_ci goto out; 3478c2ecf20Sopenharmony_ci } 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci dcr_base = dcr_resource_start(dn, 0); 3508c2ecf20Sopenharmony_ci dcr_len = dcr_resource_len(dn, 0); 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci if (dcr_base == 0 || dcr_len == 0) { 3538c2ecf20Sopenharmony_ci printk(KERN_ERR 3548c2ecf20Sopenharmony_ci "axon_msi: couldn't parse dcr properties on %pOF\n", 3558c2ecf20Sopenharmony_ci dn); 3568c2ecf20Sopenharmony_ci goto out_free_msic; 3578c2ecf20Sopenharmony_ci } 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci msic->dcr_host = dcr_map(dn, dcr_base, dcr_len); 3608c2ecf20Sopenharmony_ci if (!DCR_MAP_OK(msic->dcr_host)) { 3618c2ecf20Sopenharmony_ci printk(KERN_ERR "axon_msi: dcr_map failed for %pOF\n", 3628c2ecf20Sopenharmony_ci dn); 3638c2ecf20Sopenharmony_ci goto out_free_msic; 3648c2ecf20Sopenharmony_ci } 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci msic->fifo_virt = dma_alloc_coherent(&device->dev, MSIC_FIFO_SIZE_BYTES, 3678c2ecf20Sopenharmony_ci &msic->fifo_phys, GFP_KERNEL); 3688c2ecf20Sopenharmony_ci if (!msic->fifo_virt) { 3698c2ecf20Sopenharmony_ci printk(KERN_ERR "axon_msi: couldn't allocate fifo for %pOF\n", 3708c2ecf20Sopenharmony_ci dn); 3718c2ecf20Sopenharmony_ci goto out_free_msic; 3728c2ecf20Sopenharmony_ci } 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci virq = irq_of_parse_and_map(dn, 0); 3758c2ecf20Sopenharmony_ci if (!virq) { 3768c2ecf20Sopenharmony_ci printk(KERN_ERR "axon_msi: irq parse and map failed for %pOF\n", 3778c2ecf20Sopenharmony_ci dn); 3788c2ecf20Sopenharmony_ci goto out_free_fifo; 3798c2ecf20Sopenharmony_ci } 3808c2ecf20Sopenharmony_ci memset(msic->fifo_virt, 0xff, MSIC_FIFO_SIZE_BYTES); 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci /* We rely on being able to stash a virq in a u16, so limit irqs to < 65536 */ 3838c2ecf20Sopenharmony_ci msic->irq_domain = irq_domain_add_nomap(dn, 65536, &msic_host_ops, msic); 3848c2ecf20Sopenharmony_ci if (!msic->irq_domain) { 3858c2ecf20Sopenharmony_ci printk(KERN_ERR "axon_msi: couldn't allocate irq_domain for %pOF\n", 3868c2ecf20Sopenharmony_ci dn); 3878c2ecf20Sopenharmony_ci goto out_free_fifo; 3888c2ecf20Sopenharmony_ci } 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci irq_set_handler_data(virq, msic); 3918c2ecf20Sopenharmony_ci irq_set_chained_handler(virq, axon_msi_cascade); 3928c2ecf20Sopenharmony_ci pr_devel("axon_msi: irq 0x%x setup for axon_msi\n", virq); 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci /* Enable the MSIC hardware */ 3958c2ecf20Sopenharmony_ci msic_dcr_write(msic, MSIC_BASE_ADDR_HI_REG, msic->fifo_phys >> 32); 3968c2ecf20Sopenharmony_ci msic_dcr_write(msic, MSIC_BASE_ADDR_LO_REG, 3978c2ecf20Sopenharmony_ci msic->fifo_phys & 0xFFFFFFFF); 3988c2ecf20Sopenharmony_ci msic_dcr_write(msic, MSIC_CTRL_REG, 3998c2ecf20Sopenharmony_ci MSIC_CTRL_IRQ_ENABLE | MSIC_CTRL_ENABLE | 4008c2ecf20Sopenharmony_ci MSIC_CTRL_FIFO_SIZE); 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci msic->read_offset = dcr_read(msic->dcr_host, MSIC_WRITE_OFFSET_REG) 4038c2ecf20Sopenharmony_ci & MSIC_FIFO_SIZE_MASK; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci dev_set_drvdata(&device->dev, msic); 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci cell_pci_controller_ops.setup_msi_irqs = axon_msi_setup_msi_irqs; 4088c2ecf20Sopenharmony_ci cell_pci_controller_ops.teardown_msi_irqs = axon_msi_teardown_msi_irqs; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci axon_msi_debug_setup(dn, msic); 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci printk(KERN_DEBUG "axon_msi: setup MSIC on %pOF\n", dn); 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci return 0; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ciout_free_fifo: 4178c2ecf20Sopenharmony_ci dma_free_coherent(&device->dev, MSIC_FIFO_SIZE_BYTES, msic->fifo_virt, 4188c2ecf20Sopenharmony_ci msic->fifo_phys); 4198c2ecf20Sopenharmony_ciout_free_msic: 4208c2ecf20Sopenharmony_ci kfree(msic); 4218c2ecf20Sopenharmony_ciout: 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci return -1; 4248c2ecf20Sopenharmony_ci} 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_cistatic const struct of_device_id axon_msi_device_id[] = { 4278c2ecf20Sopenharmony_ci { 4288c2ecf20Sopenharmony_ci .compatible = "ibm,axon-msic" 4298c2ecf20Sopenharmony_ci }, 4308c2ecf20Sopenharmony_ci {} 4318c2ecf20Sopenharmony_ci}; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_cistatic struct platform_driver axon_msi_driver = { 4348c2ecf20Sopenharmony_ci .probe = axon_msi_probe, 4358c2ecf20Sopenharmony_ci .shutdown = axon_msi_shutdown, 4368c2ecf20Sopenharmony_ci .driver = { 4378c2ecf20Sopenharmony_ci .name = "axon-msi", 4388c2ecf20Sopenharmony_ci .of_match_table = axon_msi_device_id, 4398c2ecf20Sopenharmony_ci }, 4408c2ecf20Sopenharmony_ci}; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_cistatic int __init axon_msi_init(void) 4438c2ecf20Sopenharmony_ci{ 4448c2ecf20Sopenharmony_ci return platform_driver_register(&axon_msi_driver); 4458c2ecf20Sopenharmony_ci} 4468c2ecf20Sopenharmony_cisubsys_initcall(axon_msi_init); 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci#ifdef DEBUG 4508c2ecf20Sopenharmony_cistatic int msic_set(void *data, u64 val) 4518c2ecf20Sopenharmony_ci{ 4528c2ecf20Sopenharmony_ci struct axon_msic *msic = data; 4538c2ecf20Sopenharmony_ci out_le32(msic->trigger, val); 4548c2ecf20Sopenharmony_ci return 0; 4558c2ecf20Sopenharmony_ci} 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_cistatic int msic_get(void *data, u64 *val) 4588c2ecf20Sopenharmony_ci{ 4598c2ecf20Sopenharmony_ci *val = 0; 4608c2ecf20Sopenharmony_ci return 0; 4618c2ecf20Sopenharmony_ci} 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ciDEFINE_SIMPLE_ATTRIBUTE(fops_msic, msic_get, msic_set, "%llu\n"); 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_civoid axon_msi_debug_setup(struct device_node *dn, struct axon_msic *msic) 4668c2ecf20Sopenharmony_ci{ 4678c2ecf20Sopenharmony_ci char name[8]; 4688c2ecf20Sopenharmony_ci u64 addr; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci addr = of_translate_address(dn, of_get_property(dn, "reg", NULL)); 4718c2ecf20Sopenharmony_ci if (addr == OF_BAD_ADDR) { 4728c2ecf20Sopenharmony_ci pr_devel("axon_msi: couldn't translate reg property\n"); 4738c2ecf20Sopenharmony_ci return; 4748c2ecf20Sopenharmony_ci } 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci msic->trigger = ioremap(addr, 0x4); 4778c2ecf20Sopenharmony_ci if (!msic->trigger) { 4788c2ecf20Sopenharmony_ci pr_devel("axon_msi: ioremap failed\n"); 4798c2ecf20Sopenharmony_ci return; 4808c2ecf20Sopenharmony_ci } 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci snprintf(name, sizeof(name), "msic_%d", of_node_to_nid(dn)); 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci debugfs_create_file(name, 0600, powerpc_debugfs_root, msic, &fops_msic); 4858c2ecf20Sopenharmony_ci} 4868c2ecf20Sopenharmony_ci#endif /* DEBUG */ 487