18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright 2011-2012, Meador Inge, Mentor Graphics Corporation. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Some ideas based on un-pushed work done by Vivek Mahajan, Jason Jin, and 68c2ecf20Sopenharmony_ci * Mingkai Hu from Freescale Semiconductor, Inc. 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/list.h> 108c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 118c2ecf20Sopenharmony_ci#include <linux/errno.h> 128c2ecf20Sopenharmony_ci#include <linux/err.h> 138c2ecf20Sopenharmony_ci#include <linux/export.h> 148c2ecf20Sopenharmony_ci#include <linux/slab.h> 158c2ecf20Sopenharmony_ci#include <asm/prom.h> 168c2ecf20Sopenharmony_ci#include <asm/hw_irq.h> 178c2ecf20Sopenharmony_ci#include <asm/ppc-pci.h> 188c2ecf20Sopenharmony_ci#include <asm/mpic_msgr.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#define MPIC_MSGR_REGISTERS_PER_BLOCK 4 218c2ecf20Sopenharmony_ci#define MPIC_MSGR_STRIDE 0x10 228c2ecf20Sopenharmony_ci#define MPIC_MSGR_MER_OFFSET 0x100 238c2ecf20Sopenharmony_ci#define MSGR_INUSE 0 248c2ecf20Sopenharmony_ci#define MSGR_FREE 1 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistatic struct mpic_msgr **mpic_msgrs; 278c2ecf20Sopenharmony_cistatic unsigned int mpic_msgr_count; 288c2ecf20Sopenharmony_cistatic DEFINE_RAW_SPINLOCK(msgrs_lock); 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistatic inline void _mpic_msgr_mer_write(struct mpic_msgr *msgr, u32 value) 318c2ecf20Sopenharmony_ci{ 328c2ecf20Sopenharmony_ci out_be32(msgr->mer, value); 338c2ecf20Sopenharmony_ci} 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic inline u32 _mpic_msgr_mer_read(struct mpic_msgr *msgr) 368c2ecf20Sopenharmony_ci{ 378c2ecf20Sopenharmony_ci return in_be32(msgr->mer); 388c2ecf20Sopenharmony_ci} 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic inline void _mpic_msgr_disable(struct mpic_msgr *msgr) 418c2ecf20Sopenharmony_ci{ 428c2ecf20Sopenharmony_ci u32 mer = _mpic_msgr_mer_read(msgr); 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci _mpic_msgr_mer_write(msgr, mer & ~(1 << msgr->num)); 458c2ecf20Sopenharmony_ci} 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistruct mpic_msgr *mpic_msgr_get(unsigned int reg_num) 488c2ecf20Sopenharmony_ci{ 498c2ecf20Sopenharmony_ci unsigned long flags; 508c2ecf20Sopenharmony_ci struct mpic_msgr *msgr; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci /* Assume busy until proven otherwise. */ 538c2ecf20Sopenharmony_ci msgr = ERR_PTR(-EBUSY); 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci if (reg_num >= mpic_msgr_count) 568c2ecf20Sopenharmony_ci return ERR_PTR(-ENODEV); 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&msgrs_lock, flags); 598c2ecf20Sopenharmony_ci msgr = mpic_msgrs[reg_num]; 608c2ecf20Sopenharmony_ci if (msgr->in_use == MSGR_FREE) 618c2ecf20Sopenharmony_ci msgr->in_use = MSGR_INUSE; 628c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&msgrs_lock, flags); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci return msgr; 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mpic_msgr_get); 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_civoid mpic_msgr_put(struct mpic_msgr *msgr) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci unsigned long flags; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&msgr->lock, flags); 738c2ecf20Sopenharmony_ci msgr->in_use = MSGR_FREE; 748c2ecf20Sopenharmony_ci _mpic_msgr_disable(msgr); 758c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&msgr->lock, flags); 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mpic_msgr_put); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_civoid mpic_msgr_enable(struct mpic_msgr *msgr) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci unsigned long flags; 828c2ecf20Sopenharmony_ci u32 mer; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&msgr->lock, flags); 858c2ecf20Sopenharmony_ci mer = _mpic_msgr_mer_read(msgr); 868c2ecf20Sopenharmony_ci _mpic_msgr_mer_write(msgr, mer | (1 << msgr->num)); 878c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&msgr->lock, flags); 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mpic_msgr_enable); 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_civoid mpic_msgr_disable(struct mpic_msgr *msgr) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci unsigned long flags; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&msgr->lock, flags); 968c2ecf20Sopenharmony_ci _mpic_msgr_disable(msgr); 978c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&msgr->lock, flags); 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mpic_msgr_disable); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci/* The following three functions are used to compute the order and number of 1028c2ecf20Sopenharmony_ci * the message register blocks. They are clearly very inefficent. However, 1038c2ecf20Sopenharmony_ci * they are called *only* a few times during device initialization. 1048c2ecf20Sopenharmony_ci */ 1058c2ecf20Sopenharmony_cistatic unsigned int mpic_msgr_number_of_blocks(void) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci unsigned int count; 1088c2ecf20Sopenharmony_ci struct device_node *aliases; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci count = 0; 1118c2ecf20Sopenharmony_ci aliases = of_find_node_by_name(NULL, "aliases"); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci if (aliases) { 1148c2ecf20Sopenharmony_ci char buf[32]; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci for (;;) { 1178c2ecf20Sopenharmony_ci snprintf(buf, sizeof(buf), "mpic-msgr-block%d", count); 1188c2ecf20Sopenharmony_ci if (!of_find_property(aliases, buf, NULL)) 1198c2ecf20Sopenharmony_ci break; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci count += 1; 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci return count; 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic unsigned int mpic_msgr_number_of_registers(void) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci return mpic_msgr_number_of_blocks() * MPIC_MSGR_REGISTERS_PER_BLOCK; 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistatic int mpic_msgr_block_number(struct device_node *node) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci struct device_node *aliases; 1368c2ecf20Sopenharmony_ci unsigned int index, number_of_blocks; 1378c2ecf20Sopenharmony_ci char buf[64]; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci number_of_blocks = mpic_msgr_number_of_blocks(); 1408c2ecf20Sopenharmony_ci aliases = of_find_node_by_name(NULL, "aliases"); 1418c2ecf20Sopenharmony_ci if (!aliases) 1428c2ecf20Sopenharmony_ci return -1; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci for (index = 0; index < number_of_blocks; ++index) { 1458c2ecf20Sopenharmony_ci struct property *prop; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci snprintf(buf, sizeof(buf), "mpic-msgr-block%d", index); 1488c2ecf20Sopenharmony_ci prop = of_find_property(aliases, buf, NULL); 1498c2ecf20Sopenharmony_ci if (node == of_find_node_by_path(prop->value)) 1508c2ecf20Sopenharmony_ci break; 1518c2ecf20Sopenharmony_ci } 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci return index == number_of_blocks ? -1 : index; 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci/* The probe function for a single message register block. 1578c2ecf20Sopenharmony_ci */ 1588c2ecf20Sopenharmony_cistatic int mpic_msgr_probe(struct platform_device *dev) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci void __iomem *msgr_block_addr; 1618c2ecf20Sopenharmony_ci int block_number; 1628c2ecf20Sopenharmony_ci struct resource rsrc; 1638c2ecf20Sopenharmony_ci unsigned int i; 1648c2ecf20Sopenharmony_ci unsigned int irq_index; 1658c2ecf20Sopenharmony_ci struct device_node *np = dev->dev.of_node; 1668c2ecf20Sopenharmony_ci unsigned int receive_mask; 1678c2ecf20Sopenharmony_ci const unsigned int *prop; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci if (!np) { 1708c2ecf20Sopenharmony_ci dev_err(&dev->dev, "Device OF-Node is NULL"); 1718c2ecf20Sopenharmony_ci return -EFAULT; 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci /* Allocate the message register array upon the first device 1758c2ecf20Sopenharmony_ci * registered. 1768c2ecf20Sopenharmony_ci */ 1778c2ecf20Sopenharmony_ci if (!mpic_msgrs) { 1788c2ecf20Sopenharmony_ci mpic_msgr_count = mpic_msgr_number_of_registers(); 1798c2ecf20Sopenharmony_ci dev_info(&dev->dev, "Found %d message registers\n", 1808c2ecf20Sopenharmony_ci mpic_msgr_count); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci mpic_msgrs = kcalloc(mpic_msgr_count, sizeof(*mpic_msgrs), 1838c2ecf20Sopenharmony_ci GFP_KERNEL); 1848c2ecf20Sopenharmony_ci if (!mpic_msgrs) { 1858c2ecf20Sopenharmony_ci dev_err(&dev->dev, 1868c2ecf20Sopenharmony_ci "No memory for message register blocks\n"); 1878c2ecf20Sopenharmony_ci return -ENOMEM; 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci } 1908c2ecf20Sopenharmony_ci dev_info(&dev->dev, "Of-device full name %pOF\n", np); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci /* IO map the message register block. */ 1938c2ecf20Sopenharmony_ci of_address_to_resource(np, 0, &rsrc); 1948c2ecf20Sopenharmony_ci msgr_block_addr = devm_ioremap(&dev->dev, rsrc.start, resource_size(&rsrc)); 1958c2ecf20Sopenharmony_ci if (!msgr_block_addr) { 1968c2ecf20Sopenharmony_ci dev_err(&dev->dev, "Failed to iomap MPIC message registers"); 1978c2ecf20Sopenharmony_ci return -EFAULT; 1988c2ecf20Sopenharmony_ci } 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci /* Ensure the block has a defined order. */ 2018c2ecf20Sopenharmony_ci block_number = mpic_msgr_block_number(np); 2028c2ecf20Sopenharmony_ci if (block_number < 0) { 2038c2ecf20Sopenharmony_ci dev_err(&dev->dev, 2048c2ecf20Sopenharmony_ci "Failed to find message register block alias\n"); 2058c2ecf20Sopenharmony_ci return -ENODEV; 2068c2ecf20Sopenharmony_ci } 2078c2ecf20Sopenharmony_ci dev_info(&dev->dev, "Setting up message register block %d\n", 2088c2ecf20Sopenharmony_ci block_number); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci /* Grab the receive mask which specifies what registers can receive 2118c2ecf20Sopenharmony_ci * interrupts. 2128c2ecf20Sopenharmony_ci */ 2138c2ecf20Sopenharmony_ci prop = of_get_property(np, "mpic-msgr-receive-mask", NULL); 2148c2ecf20Sopenharmony_ci receive_mask = (prop) ? *prop : 0xF; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci /* Build up the appropriate message register data structures. */ 2178c2ecf20Sopenharmony_ci for (i = 0, irq_index = 0; i < MPIC_MSGR_REGISTERS_PER_BLOCK; ++i) { 2188c2ecf20Sopenharmony_ci struct mpic_msgr *msgr; 2198c2ecf20Sopenharmony_ci unsigned int reg_number; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci msgr = kzalloc(sizeof(struct mpic_msgr), GFP_KERNEL); 2228c2ecf20Sopenharmony_ci if (!msgr) { 2238c2ecf20Sopenharmony_ci dev_err(&dev->dev, "No memory for message register\n"); 2248c2ecf20Sopenharmony_ci return -ENOMEM; 2258c2ecf20Sopenharmony_ci } 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci reg_number = block_number * MPIC_MSGR_REGISTERS_PER_BLOCK + i; 2288c2ecf20Sopenharmony_ci msgr->base = msgr_block_addr + i * MPIC_MSGR_STRIDE; 2298c2ecf20Sopenharmony_ci msgr->mer = (u32 *)((u8 *)msgr->base + MPIC_MSGR_MER_OFFSET); 2308c2ecf20Sopenharmony_ci msgr->in_use = MSGR_FREE; 2318c2ecf20Sopenharmony_ci msgr->num = i; 2328c2ecf20Sopenharmony_ci raw_spin_lock_init(&msgr->lock); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci if (receive_mask & (1 << i)) { 2358c2ecf20Sopenharmony_ci msgr->irq = irq_of_parse_and_map(np, irq_index); 2368c2ecf20Sopenharmony_ci if (!msgr->irq) { 2378c2ecf20Sopenharmony_ci dev_err(&dev->dev, 2388c2ecf20Sopenharmony_ci "Missing interrupt specifier"); 2398c2ecf20Sopenharmony_ci kfree(msgr); 2408c2ecf20Sopenharmony_ci return -EFAULT; 2418c2ecf20Sopenharmony_ci } 2428c2ecf20Sopenharmony_ci irq_index += 1; 2438c2ecf20Sopenharmony_ci } else { 2448c2ecf20Sopenharmony_ci msgr->irq = 0; 2458c2ecf20Sopenharmony_ci } 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci mpic_msgrs[reg_number] = msgr; 2488c2ecf20Sopenharmony_ci mpic_msgr_disable(msgr); 2498c2ecf20Sopenharmony_ci dev_info(&dev->dev, "Register %d initialized: irq %d\n", 2508c2ecf20Sopenharmony_ci reg_number, msgr->irq); 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci } 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci return 0; 2558c2ecf20Sopenharmony_ci} 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_cistatic const struct of_device_id mpic_msgr_ids[] = { 2588c2ecf20Sopenharmony_ci { 2598c2ecf20Sopenharmony_ci .compatible = "fsl,mpic-v3.1-msgr", 2608c2ecf20Sopenharmony_ci .data = NULL, 2618c2ecf20Sopenharmony_ci }, 2628c2ecf20Sopenharmony_ci {} 2638c2ecf20Sopenharmony_ci}; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_cistatic struct platform_driver mpic_msgr_driver = { 2668c2ecf20Sopenharmony_ci .driver = { 2678c2ecf20Sopenharmony_ci .name = "mpic-msgr", 2688c2ecf20Sopenharmony_ci .of_match_table = mpic_msgr_ids, 2698c2ecf20Sopenharmony_ci }, 2708c2ecf20Sopenharmony_ci .probe = mpic_msgr_probe, 2718c2ecf20Sopenharmony_ci}; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_cistatic __init int mpic_msgr_init(void) 2748c2ecf20Sopenharmony_ci{ 2758c2ecf20Sopenharmony_ci return platform_driver_register(&mpic_msgr_driver); 2768c2ecf20Sopenharmony_ci} 2778c2ecf20Sopenharmony_cisubsys_initcall(mpic_msgr_init); 278