1b886d83cSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 28626816eSJia Hongtao /* 38626816eSJia Hongtao * Copyright 2011-2012, Meador Inge, Mentor Graphics Corporation. 48626816eSJia Hongtao * 58626816eSJia Hongtao * Some ideas based on un-pushed work done by Vivek Mahajan, Jason Jin, and 68626816eSJia Hongtao * Mingkai Hu from Freescale Semiconductor, Inc. 78626816eSJia Hongtao */ 88626816eSJia Hongtao 98626816eSJia Hongtao #include <linux/list.h> 10e6f6390aSChristophe Leroy #include <linux/of_address.h> 11e6f6390aSChristophe Leroy #include <linux/of_irq.h> 128626816eSJia Hongtao #include <linux/of_platform.h> 138626816eSJia Hongtao #include <linux/errno.h> 1446c5c59eSScott Wood #include <linux/err.h> 1546c5c59eSScott Wood #include <linux/export.h> 1646c5c59eSScott Wood #include <linux/slab.h> 178626816eSJia Hongtao #include <asm/hw_irq.h> 188626816eSJia Hongtao #include <asm/ppc-pci.h> 198626816eSJia Hongtao #include <asm/mpic_msgr.h> 208626816eSJia Hongtao 218626816eSJia Hongtao #define MPIC_MSGR_REGISTERS_PER_BLOCK 4 228626816eSJia Hongtao #define MPIC_MSGR_STRIDE 0x10 23*16a3f41fSruanjinjie #define MPIC_MSGR_MER_OFFSET (0x100 / sizeof(u32)) 248626816eSJia Hongtao #define MSGR_INUSE 0 258626816eSJia Hongtao #define MSGR_FREE 1 268626816eSJia Hongtao 278626816eSJia Hongtao static struct mpic_msgr **mpic_msgrs; 288626816eSJia Hongtao static unsigned int mpic_msgr_count; 29e0a5a6c3SMingkai Hu static DEFINE_RAW_SPINLOCK(msgrs_lock); 308626816eSJia Hongtao 318626816eSJia Hongtao static inline void _mpic_msgr_mer_write(struct mpic_msgr *msgr, u32 value) 328626816eSJia Hongtao { 338626816eSJia Hongtao out_be32(msgr->mer, value); 348626816eSJia Hongtao } 358626816eSJia Hongtao 368626816eSJia Hongtao static inline u32 _mpic_msgr_mer_read(struct mpic_msgr *msgr) 378626816eSJia Hongtao { 388626816eSJia Hongtao return in_be32(msgr->mer); 398626816eSJia Hongtao } 408626816eSJia Hongtao 418626816eSJia Hongtao static inline void _mpic_msgr_disable(struct mpic_msgr *msgr) 428626816eSJia Hongtao { 438626816eSJia Hongtao u32 mer = _mpic_msgr_mer_read(msgr); 448626816eSJia Hongtao 458626816eSJia Hongtao _mpic_msgr_mer_write(msgr, mer & ~(1 << msgr->num)); 468626816eSJia Hongtao } 478626816eSJia Hongtao 488626816eSJia Hongtao struct mpic_msgr *mpic_msgr_get(unsigned int reg_num) 498626816eSJia Hongtao { 508626816eSJia Hongtao unsigned long flags; 518626816eSJia Hongtao struct mpic_msgr *msgr; 528626816eSJia Hongtao 538626816eSJia Hongtao /* Assume busy until proven otherwise. */ 548626816eSJia Hongtao msgr = ERR_PTR(-EBUSY); 558626816eSJia Hongtao 568626816eSJia Hongtao if (reg_num >= mpic_msgr_count) 578626816eSJia Hongtao return ERR_PTR(-ENODEV); 588626816eSJia Hongtao 59e0a5a6c3SMingkai Hu raw_spin_lock_irqsave(&msgrs_lock, flags); 608626816eSJia Hongtao msgr = mpic_msgrs[reg_num]; 61e0a5a6c3SMingkai Hu if (msgr->in_use == MSGR_FREE) 628626816eSJia Hongtao msgr->in_use = MSGR_INUSE; 63e0a5a6c3SMingkai Hu raw_spin_unlock_irqrestore(&msgrs_lock, flags); 648626816eSJia Hongtao 658626816eSJia Hongtao return msgr; 668626816eSJia Hongtao } 678626816eSJia Hongtao EXPORT_SYMBOL_GPL(mpic_msgr_get); 688626816eSJia Hongtao 698626816eSJia Hongtao void mpic_msgr_put(struct mpic_msgr *msgr) 708626816eSJia Hongtao { 718626816eSJia Hongtao unsigned long flags; 728626816eSJia Hongtao 738626816eSJia Hongtao raw_spin_lock_irqsave(&msgr->lock, flags); 748626816eSJia Hongtao msgr->in_use = MSGR_FREE; 758626816eSJia Hongtao _mpic_msgr_disable(msgr); 768626816eSJia Hongtao raw_spin_unlock_irqrestore(&msgr->lock, flags); 778626816eSJia Hongtao } 788626816eSJia Hongtao EXPORT_SYMBOL_GPL(mpic_msgr_put); 798626816eSJia Hongtao 808626816eSJia Hongtao void mpic_msgr_enable(struct mpic_msgr *msgr) 818626816eSJia Hongtao { 828626816eSJia Hongtao unsigned long flags; 838626816eSJia Hongtao u32 mer; 848626816eSJia Hongtao 858626816eSJia Hongtao raw_spin_lock_irqsave(&msgr->lock, flags); 868626816eSJia Hongtao mer = _mpic_msgr_mer_read(msgr); 878626816eSJia Hongtao _mpic_msgr_mer_write(msgr, mer | (1 << msgr->num)); 888626816eSJia Hongtao raw_spin_unlock_irqrestore(&msgr->lock, flags); 898626816eSJia Hongtao } 908626816eSJia Hongtao EXPORT_SYMBOL_GPL(mpic_msgr_enable); 918626816eSJia Hongtao 928626816eSJia Hongtao void mpic_msgr_disable(struct mpic_msgr *msgr) 938626816eSJia Hongtao { 948626816eSJia Hongtao unsigned long flags; 958626816eSJia Hongtao 968626816eSJia Hongtao raw_spin_lock_irqsave(&msgr->lock, flags); 978626816eSJia Hongtao _mpic_msgr_disable(msgr); 988626816eSJia Hongtao raw_spin_unlock_irqrestore(&msgr->lock, flags); 998626816eSJia Hongtao } 1008626816eSJia Hongtao EXPORT_SYMBOL_GPL(mpic_msgr_disable); 1018626816eSJia Hongtao 1028626816eSJia Hongtao /* The following three functions are used to compute the order and number of 1031fd02f66SJulia Lawall * the message register blocks. They are clearly very inefficient. However, 1048626816eSJia Hongtao * they are called *only* a few times during device initialization. 1058626816eSJia Hongtao */ 1068626816eSJia Hongtao static unsigned int mpic_msgr_number_of_blocks(void) 1078626816eSJia Hongtao { 1088626816eSJia Hongtao unsigned int count; 1098626816eSJia Hongtao struct device_node *aliases; 1108626816eSJia Hongtao 1118626816eSJia Hongtao count = 0; 1128626816eSJia Hongtao aliases = of_find_node_by_name(NULL, "aliases"); 1138626816eSJia Hongtao 1148626816eSJia Hongtao if (aliases) { 1158626816eSJia Hongtao char buf[32]; 1168626816eSJia Hongtao 1178626816eSJia Hongtao for (;;) { 1188626816eSJia Hongtao snprintf(buf, sizeof(buf), "mpic-msgr-block%d", count); 1198626816eSJia Hongtao if (!of_find_property(aliases, buf, NULL)) 1208626816eSJia Hongtao break; 1218626816eSJia Hongtao 1228626816eSJia Hongtao count += 1; 1238626816eSJia Hongtao } 1243d31adc4SLiang He of_node_put(aliases); 1258626816eSJia Hongtao } 1268626816eSJia Hongtao 1278626816eSJia Hongtao return count; 1288626816eSJia Hongtao } 1298626816eSJia Hongtao 1308626816eSJia Hongtao static unsigned int mpic_msgr_number_of_registers(void) 1318626816eSJia Hongtao { 1328626816eSJia Hongtao return mpic_msgr_number_of_blocks() * MPIC_MSGR_REGISTERS_PER_BLOCK; 1338626816eSJia Hongtao } 1348626816eSJia Hongtao 1358626816eSJia Hongtao static int mpic_msgr_block_number(struct device_node *node) 1368626816eSJia Hongtao { 1378626816eSJia Hongtao struct device_node *aliases; 1388626816eSJia Hongtao unsigned int index, number_of_blocks; 1398626816eSJia Hongtao char buf[64]; 1408626816eSJia Hongtao 1418626816eSJia Hongtao number_of_blocks = mpic_msgr_number_of_blocks(); 1428626816eSJia Hongtao aliases = of_find_node_by_name(NULL, "aliases"); 1438626816eSJia Hongtao if (!aliases) 1448626816eSJia Hongtao return -1; 1458626816eSJia Hongtao 1468626816eSJia Hongtao for (index = 0; index < number_of_blocks; ++index) { 1478626816eSJia Hongtao struct property *prop; 1483d31adc4SLiang He struct device_node *tn; 1498626816eSJia Hongtao 1508626816eSJia Hongtao snprintf(buf, sizeof(buf), "mpic-msgr-block%d", index); 1518626816eSJia Hongtao prop = of_find_property(aliases, buf, NULL); 1523d31adc4SLiang He tn = of_find_node_by_path(prop->value); 1533d31adc4SLiang He if (node == tn) { 1543d31adc4SLiang He of_node_put(tn); 1558626816eSJia Hongtao break; 1568626816eSJia Hongtao } 1573d31adc4SLiang He of_node_put(tn); 1583d31adc4SLiang He } 1593d31adc4SLiang He of_node_put(aliases); 1608626816eSJia Hongtao 1618626816eSJia Hongtao return index == number_of_blocks ? -1 : index; 1628626816eSJia Hongtao } 1638626816eSJia Hongtao 1648626816eSJia Hongtao /* The probe function for a single message register block. 1658626816eSJia Hongtao */ 166cad5cef6SGreg Kroah-Hartman static int mpic_msgr_probe(struct platform_device *dev) 1678626816eSJia Hongtao { 1688626816eSJia Hongtao void __iomem *msgr_block_addr; 1698626816eSJia Hongtao int block_number; 1708626816eSJia Hongtao struct resource rsrc; 1718626816eSJia Hongtao unsigned int i; 1728626816eSJia Hongtao unsigned int irq_index; 1738626816eSJia Hongtao struct device_node *np = dev->dev.of_node; 1748626816eSJia Hongtao unsigned int receive_mask; 1758626816eSJia Hongtao const unsigned int *prop; 1768626816eSJia Hongtao 1778626816eSJia Hongtao if (!np) { 1788626816eSJia Hongtao dev_err(&dev->dev, "Device OF-Node is NULL"); 1798626816eSJia Hongtao return -EFAULT; 1808626816eSJia Hongtao } 1818626816eSJia Hongtao 1828626816eSJia Hongtao /* Allocate the message register array upon the first device 1838626816eSJia Hongtao * registered. 1848626816eSJia Hongtao */ 1858626816eSJia Hongtao if (!mpic_msgrs) { 1868626816eSJia Hongtao mpic_msgr_count = mpic_msgr_number_of_registers(); 1878626816eSJia Hongtao dev_info(&dev->dev, "Found %d message registers\n", 1888626816eSJia Hongtao mpic_msgr_count); 1898626816eSJia Hongtao 190983e2444SHimangi Saraogi mpic_msgrs = kcalloc(mpic_msgr_count, sizeof(*mpic_msgrs), 1918626816eSJia Hongtao GFP_KERNEL); 1928626816eSJia Hongtao if (!mpic_msgrs) { 1938626816eSJia Hongtao dev_err(&dev->dev, 1948626816eSJia Hongtao "No memory for message register blocks\n"); 1958626816eSJia Hongtao return -ENOMEM; 1968626816eSJia Hongtao } 1978626816eSJia Hongtao } 198b7c670d6SRob Herring dev_info(&dev->dev, "Of-device full name %pOF\n", np); 1998626816eSJia Hongtao 2008626816eSJia Hongtao /* IO map the message register block. */ 2018626816eSJia Hongtao of_address_to_resource(np, 0, &rsrc); 202ffa17970SQinglang Miao msgr_block_addr = devm_ioremap(&dev->dev, rsrc.start, resource_size(&rsrc)); 2038626816eSJia Hongtao if (!msgr_block_addr) { 2048626816eSJia Hongtao dev_err(&dev->dev, "Failed to iomap MPIC message registers"); 2058626816eSJia Hongtao return -EFAULT; 2068626816eSJia Hongtao } 2078626816eSJia Hongtao 2088626816eSJia Hongtao /* Ensure the block has a defined order. */ 2098626816eSJia Hongtao block_number = mpic_msgr_block_number(np); 2108626816eSJia Hongtao if (block_number < 0) { 2118626816eSJia Hongtao dev_err(&dev->dev, 2128626816eSJia Hongtao "Failed to find message register block alias\n"); 2138626816eSJia Hongtao return -ENODEV; 2148626816eSJia Hongtao } 2158626816eSJia Hongtao dev_info(&dev->dev, "Setting up message register block %d\n", 2168626816eSJia Hongtao block_number); 2178626816eSJia Hongtao 2188626816eSJia Hongtao /* Grab the receive mask which specifies what registers can receive 2198626816eSJia Hongtao * interrupts. 2208626816eSJia Hongtao */ 2218626816eSJia Hongtao prop = of_get_property(np, "mpic-msgr-receive-mask", NULL); 2228626816eSJia Hongtao receive_mask = (prop) ? *prop : 0xF; 2238626816eSJia Hongtao 2248626816eSJia Hongtao /* Build up the appropriate message register data structures. */ 2258626816eSJia Hongtao for (i = 0, irq_index = 0; i < MPIC_MSGR_REGISTERS_PER_BLOCK; ++i) { 2268626816eSJia Hongtao struct mpic_msgr *msgr; 2278626816eSJia Hongtao unsigned int reg_number; 2288626816eSJia Hongtao 2298626816eSJia Hongtao msgr = kzalloc(sizeof(struct mpic_msgr), GFP_KERNEL); 2308626816eSJia Hongtao if (!msgr) { 2318626816eSJia Hongtao dev_err(&dev->dev, "No memory for message register\n"); 2328626816eSJia Hongtao return -ENOMEM; 2338626816eSJia Hongtao } 2348626816eSJia Hongtao 2358626816eSJia Hongtao reg_number = block_number * MPIC_MSGR_REGISTERS_PER_BLOCK + i; 2368626816eSJia Hongtao msgr->base = msgr_block_addr + i * MPIC_MSGR_STRIDE; 237*16a3f41fSruanjinjie msgr->mer = msgr->base + MPIC_MSGR_MER_OFFSET; 2388626816eSJia Hongtao msgr->in_use = MSGR_FREE; 2398626816eSJia Hongtao msgr->num = i; 2408626816eSJia Hongtao raw_spin_lock_init(&msgr->lock); 2418626816eSJia Hongtao 2428626816eSJia Hongtao if (receive_mask & (1 << i)) { 243f7578496SThierry Reding msgr->irq = irq_of_parse_and_map(np, irq_index); 244ef24ba70SMichael Ellerman if (!msgr->irq) { 2458626816eSJia Hongtao dev_err(&dev->dev, 2468626816eSJia Hongtao "Missing interrupt specifier"); 2478626816eSJia Hongtao kfree(msgr); 2488626816eSJia Hongtao return -EFAULT; 2498626816eSJia Hongtao } 2508626816eSJia Hongtao irq_index += 1; 2518626816eSJia Hongtao } else { 252ef24ba70SMichael Ellerman msgr->irq = 0; 2538626816eSJia Hongtao } 2548626816eSJia Hongtao 2558626816eSJia Hongtao mpic_msgrs[reg_number] = msgr; 2568626816eSJia Hongtao mpic_msgr_disable(msgr); 2578626816eSJia Hongtao dev_info(&dev->dev, "Register %d initialized: irq %d\n", 2588626816eSJia Hongtao reg_number, msgr->irq); 2598626816eSJia Hongtao 2608626816eSJia Hongtao } 2618626816eSJia Hongtao 2628626816eSJia Hongtao return 0; 2638626816eSJia Hongtao } 2648626816eSJia Hongtao 2658626816eSJia Hongtao static const struct of_device_id mpic_msgr_ids[] = { 2668626816eSJia Hongtao { 2678626816eSJia Hongtao .compatible = "fsl,mpic-v3.1-msgr", 2688626816eSJia Hongtao .data = NULL, 2698626816eSJia Hongtao }, 2708626816eSJia Hongtao {} 2718626816eSJia Hongtao }; 2728626816eSJia Hongtao 2738626816eSJia Hongtao static struct platform_driver mpic_msgr_driver = { 2748626816eSJia Hongtao .driver = { 2758626816eSJia Hongtao .name = "mpic-msgr", 2768626816eSJia Hongtao .of_match_table = mpic_msgr_ids, 2778626816eSJia Hongtao }, 2788626816eSJia Hongtao .probe = mpic_msgr_probe, 2798626816eSJia Hongtao }; 2808626816eSJia Hongtao 2818626816eSJia Hongtao static __init int mpic_msgr_init(void) 2828626816eSJia Hongtao { 2838626816eSJia Hongtao return platform_driver_register(&mpic_msgr_driver); 2848626816eSJia Hongtao } 2858626816eSJia Hongtao subsys_initcall(mpic_msgr_init); 286