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> 108626816eSJia Hongtao #include <linux/of_platform.h> 118626816eSJia Hongtao #include <linux/errno.h> 1246c5c59eSScott Wood #include <linux/err.h> 1346c5c59eSScott Wood #include <linux/export.h> 1446c5c59eSScott Wood #include <linux/slab.h> 158626816eSJia Hongtao #include <asm/prom.h> 168626816eSJia Hongtao #include <asm/hw_irq.h> 178626816eSJia Hongtao #include <asm/ppc-pci.h> 188626816eSJia Hongtao #include <asm/mpic_msgr.h> 198626816eSJia Hongtao 208626816eSJia Hongtao #define MPIC_MSGR_REGISTERS_PER_BLOCK 4 218626816eSJia Hongtao #define MPIC_MSGR_STRIDE 0x10 228626816eSJia Hongtao #define MPIC_MSGR_MER_OFFSET 0x100 238626816eSJia Hongtao #define MSGR_INUSE 0 248626816eSJia Hongtao #define MSGR_FREE 1 258626816eSJia Hongtao 268626816eSJia Hongtao static struct mpic_msgr **mpic_msgrs; 278626816eSJia Hongtao static unsigned int mpic_msgr_count; 28e0a5a6c3SMingkai Hu static DEFINE_RAW_SPINLOCK(msgrs_lock); 298626816eSJia Hongtao 308626816eSJia Hongtao static inline void _mpic_msgr_mer_write(struct mpic_msgr *msgr, u32 value) 318626816eSJia Hongtao { 328626816eSJia Hongtao out_be32(msgr->mer, value); 338626816eSJia Hongtao } 348626816eSJia Hongtao 358626816eSJia Hongtao static inline u32 _mpic_msgr_mer_read(struct mpic_msgr *msgr) 368626816eSJia Hongtao { 378626816eSJia Hongtao return in_be32(msgr->mer); 388626816eSJia Hongtao } 398626816eSJia Hongtao 408626816eSJia Hongtao static inline void _mpic_msgr_disable(struct mpic_msgr *msgr) 418626816eSJia Hongtao { 428626816eSJia Hongtao u32 mer = _mpic_msgr_mer_read(msgr); 438626816eSJia Hongtao 448626816eSJia Hongtao _mpic_msgr_mer_write(msgr, mer & ~(1 << msgr->num)); 458626816eSJia Hongtao } 468626816eSJia Hongtao 478626816eSJia Hongtao struct mpic_msgr *mpic_msgr_get(unsigned int reg_num) 488626816eSJia Hongtao { 498626816eSJia Hongtao unsigned long flags; 508626816eSJia Hongtao struct mpic_msgr *msgr; 518626816eSJia Hongtao 528626816eSJia Hongtao /* Assume busy until proven otherwise. */ 538626816eSJia Hongtao msgr = ERR_PTR(-EBUSY); 548626816eSJia Hongtao 558626816eSJia Hongtao if (reg_num >= mpic_msgr_count) 568626816eSJia Hongtao return ERR_PTR(-ENODEV); 578626816eSJia Hongtao 58e0a5a6c3SMingkai Hu raw_spin_lock_irqsave(&msgrs_lock, flags); 598626816eSJia Hongtao msgr = mpic_msgrs[reg_num]; 60e0a5a6c3SMingkai Hu if (msgr->in_use == MSGR_FREE) 618626816eSJia Hongtao msgr->in_use = MSGR_INUSE; 62e0a5a6c3SMingkai Hu raw_spin_unlock_irqrestore(&msgrs_lock, flags); 638626816eSJia Hongtao 648626816eSJia Hongtao return msgr; 658626816eSJia Hongtao } 668626816eSJia Hongtao EXPORT_SYMBOL_GPL(mpic_msgr_get); 678626816eSJia Hongtao 688626816eSJia Hongtao void mpic_msgr_put(struct mpic_msgr *msgr) 698626816eSJia Hongtao { 708626816eSJia Hongtao unsigned long flags; 718626816eSJia Hongtao 728626816eSJia Hongtao raw_spin_lock_irqsave(&msgr->lock, flags); 738626816eSJia Hongtao msgr->in_use = MSGR_FREE; 748626816eSJia Hongtao _mpic_msgr_disable(msgr); 758626816eSJia Hongtao raw_spin_unlock_irqrestore(&msgr->lock, flags); 768626816eSJia Hongtao } 778626816eSJia Hongtao EXPORT_SYMBOL_GPL(mpic_msgr_put); 788626816eSJia Hongtao 798626816eSJia Hongtao void mpic_msgr_enable(struct mpic_msgr *msgr) 808626816eSJia Hongtao { 818626816eSJia Hongtao unsigned long flags; 828626816eSJia Hongtao u32 mer; 838626816eSJia Hongtao 848626816eSJia Hongtao raw_spin_lock_irqsave(&msgr->lock, flags); 858626816eSJia Hongtao mer = _mpic_msgr_mer_read(msgr); 868626816eSJia Hongtao _mpic_msgr_mer_write(msgr, mer | (1 << msgr->num)); 878626816eSJia Hongtao raw_spin_unlock_irqrestore(&msgr->lock, flags); 888626816eSJia Hongtao } 898626816eSJia Hongtao EXPORT_SYMBOL_GPL(mpic_msgr_enable); 908626816eSJia Hongtao 918626816eSJia Hongtao void mpic_msgr_disable(struct mpic_msgr *msgr) 928626816eSJia Hongtao { 938626816eSJia Hongtao unsigned long flags; 948626816eSJia Hongtao 958626816eSJia Hongtao raw_spin_lock_irqsave(&msgr->lock, flags); 968626816eSJia Hongtao _mpic_msgr_disable(msgr); 978626816eSJia Hongtao raw_spin_unlock_irqrestore(&msgr->lock, flags); 988626816eSJia Hongtao } 998626816eSJia Hongtao EXPORT_SYMBOL_GPL(mpic_msgr_disable); 1008626816eSJia Hongtao 1018626816eSJia Hongtao /* The following three functions are used to compute the order and number of 1028626816eSJia Hongtao * the message register blocks. They are clearly very inefficent. However, 1038626816eSJia Hongtao * they are called *only* a few times during device initialization. 1048626816eSJia Hongtao */ 1058626816eSJia Hongtao static unsigned int mpic_msgr_number_of_blocks(void) 1068626816eSJia Hongtao { 1078626816eSJia Hongtao unsigned int count; 1088626816eSJia Hongtao struct device_node *aliases; 1098626816eSJia Hongtao 1108626816eSJia Hongtao count = 0; 1118626816eSJia Hongtao aliases = of_find_node_by_name(NULL, "aliases"); 1128626816eSJia Hongtao 1138626816eSJia Hongtao if (aliases) { 1148626816eSJia Hongtao char buf[32]; 1158626816eSJia Hongtao 1168626816eSJia Hongtao for (;;) { 1178626816eSJia Hongtao snprintf(buf, sizeof(buf), "mpic-msgr-block%d", count); 1188626816eSJia Hongtao if (!of_find_property(aliases, buf, NULL)) 1198626816eSJia Hongtao break; 1208626816eSJia Hongtao 1218626816eSJia Hongtao count += 1; 1228626816eSJia Hongtao } 1238626816eSJia Hongtao } 1248626816eSJia Hongtao 1258626816eSJia Hongtao return count; 1268626816eSJia Hongtao } 1278626816eSJia Hongtao 1288626816eSJia Hongtao static unsigned int mpic_msgr_number_of_registers(void) 1298626816eSJia Hongtao { 1308626816eSJia Hongtao return mpic_msgr_number_of_blocks() * MPIC_MSGR_REGISTERS_PER_BLOCK; 1318626816eSJia Hongtao } 1328626816eSJia Hongtao 1338626816eSJia Hongtao static int mpic_msgr_block_number(struct device_node *node) 1348626816eSJia Hongtao { 1358626816eSJia Hongtao struct device_node *aliases; 1368626816eSJia Hongtao unsigned int index, number_of_blocks; 1378626816eSJia Hongtao char buf[64]; 1388626816eSJia Hongtao 1398626816eSJia Hongtao number_of_blocks = mpic_msgr_number_of_blocks(); 1408626816eSJia Hongtao aliases = of_find_node_by_name(NULL, "aliases"); 1418626816eSJia Hongtao if (!aliases) 1428626816eSJia Hongtao return -1; 1438626816eSJia Hongtao 1448626816eSJia Hongtao for (index = 0; index < number_of_blocks; ++index) { 1458626816eSJia Hongtao struct property *prop; 1468626816eSJia Hongtao 1478626816eSJia Hongtao snprintf(buf, sizeof(buf), "mpic-msgr-block%d", index); 1488626816eSJia Hongtao prop = of_find_property(aliases, buf, NULL); 1498626816eSJia Hongtao if (node == of_find_node_by_path(prop->value)) 1508626816eSJia Hongtao break; 1518626816eSJia Hongtao } 1528626816eSJia Hongtao 1538626816eSJia Hongtao return index == number_of_blocks ? -1 : index; 1548626816eSJia Hongtao } 1558626816eSJia Hongtao 1568626816eSJia Hongtao /* The probe function for a single message register block. 1578626816eSJia Hongtao */ 158cad5cef6SGreg Kroah-Hartman static int mpic_msgr_probe(struct platform_device *dev) 1598626816eSJia Hongtao { 1608626816eSJia Hongtao void __iomem *msgr_block_addr; 1618626816eSJia Hongtao int block_number; 1628626816eSJia Hongtao struct resource rsrc; 1638626816eSJia Hongtao unsigned int i; 1648626816eSJia Hongtao unsigned int irq_index; 1658626816eSJia Hongtao struct device_node *np = dev->dev.of_node; 1668626816eSJia Hongtao unsigned int receive_mask; 1678626816eSJia Hongtao const unsigned int *prop; 1688626816eSJia Hongtao 1698626816eSJia Hongtao if (!np) { 1708626816eSJia Hongtao dev_err(&dev->dev, "Device OF-Node is NULL"); 1718626816eSJia Hongtao return -EFAULT; 1728626816eSJia Hongtao } 1738626816eSJia Hongtao 1748626816eSJia Hongtao /* Allocate the message register array upon the first device 1758626816eSJia Hongtao * registered. 1768626816eSJia Hongtao */ 1778626816eSJia Hongtao if (!mpic_msgrs) { 1788626816eSJia Hongtao mpic_msgr_count = mpic_msgr_number_of_registers(); 1798626816eSJia Hongtao dev_info(&dev->dev, "Found %d message registers\n", 1808626816eSJia Hongtao mpic_msgr_count); 1818626816eSJia Hongtao 182983e2444SHimangi Saraogi mpic_msgrs = kcalloc(mpic_msgr_count, sizeof(*mpic_msgrs), 1838626816eSJia Hongtao GFP_KERNEL); 1848626816eSJia Hongtao if (!mpic_msgrs) { 1858626816eSJia Hongtao dev_err(&dev->dev, 1868626816eSJia Hongtao "No memory for message register blocks\n"); 1878626816eSJia Hongtao return -ENOMEM; 1888626816eSJia Hongtao } 1898626816eSJia Hongtao } 190b7c670d6SRob Herring dev_info(&dev->dev, "Of-device full name %pOF\n", np); 1918626816eSJia Hongtao 1928626816eSJia Hongtao /* IO map the message register block. */ 1938626816eSJia Hongtao of_address_to_resource(np, 0, &rsrc); 194*ffa17970SQinglang Miao msgr_block_addr = devm_ioremap(&dev->dev, rsrc.start, resource_size(&rsrc)); 1958626816eSJia Hongtao if (!msgr_block_addr) { 1968626816eSJia Hongtao dev_err(&dev->dev, "Failed to iomap MPIC message registers"); 1978626816eSJia Hongtao return -EFAULT; 1988626816eSJia Hongtao } 1998626816eSJia Hongtao 2008626816eSJia Hongtao /* Ensure the block has a defined order. */ 2018626816eSJia Hongtao block_number = mpic_msgr_block_number(np); 2028626816eSJia Hongtao if (block_number < 0) { 2038626816eSJia Hongtao dev_err(&dev->dev, 2048626816eSJia Hongtao "Failed to find message register block alias\n"); 2058626816eSJia Hongtao return -ENODEV; 2068626816eSJia Hongtao } 2078626816eSJia Hongtao dev_info(&dev->dev, "Setting up message register block %d\n", 2088626816eSJia Hongtao block_number); 2098626816eSJia Hongtao 2108626816eSJia Hongtao /* Grab the receive mask which specifies what registers can receive 2118626816eSJia Hongtao * interrupts. 2128626816eSJia Hongtao */ 2138626816eSJia Hongtao prop = of_get_property(np, "mpic-msgr-receive-mask", NULL); 2148626816eSJia Hongtao receive_mask = (prop) ? *prop : 0xF; 2158626816eSJia Hongtao 2168626816eSJia Hongtao /* Build up the appropriate message register data structures. */ 2178626816eSJia Hongtao for (i = 0, irq_index = 0; i < MPIC_MSGR_REGISTERS_PER_BLOCK; ++i) { 2188626816eSJia Hongtao struct mpic_msgr *msgr; 2198626816eSJia Hongtao unsigned int reg_number; 2208626816eSJia Hongtao 2218626816eSJia Hongtao msgr = kzalloc(sizeof(struct mpic_msgr), GFP_KERNEL); 2228626816eSJia Hongtao if (!msgr) { 2238626816eSJia Hongtao dev_err(&dev->dev, "No memory for message register\n"); 2248626816eSJia Hongtao return -ENOMEM; 2258626816eSJia Hongtao } 2268626816eSJia Hongtao 2278626816eSJia Hongtao reg_number = block_number * MPIC_MSGR_REGISTERS_PER_BLOCK + i; 2288626816eSJia Hongtao msgr->base = msgr_block_addr + i * MPIC_MSGR_STRIDE; 229dea58bd1SMingkai Hu msgr->mer = (u32 *)((u8 *)msgr->base + MPIC_MSGR_MER_OFFSET); 2308626816eSJia Hongtao msgr->in_use = MSGR_FREE; 2318626816eSJia Hongtao msgr->num = i; 2328626816eSJia Hongtao raw_spin_lock_init(&msgr->lock); 2338626816eSJia Hongtao 2348626816eSJia Hongtao if (receive_mask & (1 << i)) { 235f7578496SThierry Reding msgr->irq = irq_of_parse_and_map(np, irq_index); 236ef24ba70SMichael Ellerman if (!msgr->irq) { 2378626816eSJia Hongtao dev_err(&dev->dev, 2388626816eSJia Hongtao "Missing interrupt specifier"); 2398626816eSJia Hongtao kfree(msgr); 2408626816eSJia Hongtao return -EFAULT; 2418626816eSJia Hongtao } 2428626816eSJia Hongtao irq_index += 1; 2438626816eSJia Hongtao } else { 244ef24ba70SMichael Ellerman msgr->irq = 0; 2458626816eSJia Hongtao } 2468626816eSJia Hongtao 2478626816eSJia Hongtao mpic_msgrs[reg_number] = msgr; 2488626816eSJia Hongtao mpic_msgr_disable(msgr); 2498626816eSJia Hongtao dev_info(&dev->dev, "Register %d initialized: irq %d\n", 2508626816eSJia Hongtao reg_number, msgr->irq); 2518626816eSJia Hongtao 2528626816eSJia Hongtao } 2538626816eSJia Hongtao 2548626816eSJia Hongtao return 0; 2558626816eSJia Hongtao } 2568626816eSJia Hongtao 2578626816eSJia Hongtao static const struct of_device_id mpic_msgr_ids[] = { 2588626816eSJia Hongtao { 2598626816eSJia Hongtao .compatible = "fsl,mpic-v3.1-msgr", 2608626816eSJia Hongtao .data = NULL, 2618626816eSJia Hongtao }, 2628626816eSJia Hongtao {} 2638626816eSJia Hongtao }; 2648626816eSJia Hongtao 2658626816eSJia Hongtao static struct platform_driver mpic_msgr_driver = { 2668626816eSJia Hongtao .driver = { 2678626816eSJia Hongtao .name = "mpic-msgr", 2688626816eSJia Hongtao .of_match_table = mpic_msgr_ids, 2698626816eSJia Hongtao }, 2708626816eSJia Hongtao .probe = mpic_msgr_probe, 2718626816eSJia Hongtao }; 2728626816eSJia Hongtao 2738626816eSJia Hongtao static __init int mpic_msgr_init(void) 2748626816eSJia Hongtao { 2758626816eSJia Hongtao return platform_driver_register(&mpic_msgr_driver); 2768626816eSJia Hongtao } 2778626816eSJia Hongtao subsys_initcall(mpic_msgr_init); 278