18626816eSJia Hongtao /* 28626816eSJia Hongtao * Copyright 2011-2012, Meador Inge, Mentor Graphics Corporation. 38626816eSJia Hongtao * 48626816eSJia Hongtao * Some ideas based on un-pushed work done by Vivek Mahajan, Jason Jin, and 58626816eSJia Hongtao * Mingkai Hu from Freescale Semiconductor, Inc. 68626816eSJia Hongtao * 78626816eSJia Hongtao * This program is free software; you can redistribute it and/or 88626816eSJia Hongtao * modify it under the terms of the GNU General Public License 98626816eSJia Hongtao * as published by the Free Software Foundation; version 2 of the 108626816eSJia Hongtao * License. 118626816eSJia Hongtao * 128626816eSJia Hongtao */ 138626816eSJia Hongtao 148626816eSJia Hongtao #include <linux/list.h> 158626816eSJia Hongtao #include <linux/of_platform.h> 168626816eSJia Hongtao #include <linux/errno.h> 1746c5c59eSScott Wood #include <linux/err.h> 1846c5c59eSScott Wood #include <linux/export.h> 1946c5c59eSScott Wood #include <linux/slab.h> 208626816eSJia Hongtao #include <asm/prom.h> 218626816eSJia Hongtao #include <asm/hw_irq.h> 228626816eSJia Hongtao #include <asm/ppc-pci.h> 238626816eSJia Hongtao #include <asm/mpic_msgr.h> 248626816eSJia Hongtao 258626816eSJia Hongtao #define MPIC_MSGR_REGISTERS_PER_BLOCK 4 268626816eSJia Hongtao #define MPIC_MSGR_STRIDE 0x10 278626816eSJia Hongtao #define MPIC_MSGR_MER_OFFSET 0x100 288626816eSJia Hongtao #define MSGR_INUSE 0 298626816eSJia Hongtao #define MSGR_FREE 1 308626816eSJia Hongtao 318626816eSJia Hongtao static struct mpic_msgr **mpic_msgrs; 328626816eSJia Hongtao static unsigned int mpic_msgr_count; 33e0a5a6c3SMingkai Hu static DEFINE_RAW_SPINLOCK(msgrs_lock); 348626816eSJia Hongtao 358626816eSJia Hongtao static inline void _mpic_msgr_mer_write(struct mpic_msgr *msgr, u32 value) 368626816eSJia Hongtao { 378626816eSJia Hongtao out_be32(msgr->mer, value); 388626816eSJia Hongtao } 398626816eSJia Hongtao 408626816eSJia Hongtao static inline u32 _mpic_msgr_mer_read(struct mpic_msgr *msgr) 418626816eSJia Hongtao { 428626816eSJia Hongtao return in_be32(msgr->mer); 438626816eSJia Hongtao } 448626816eSJia Hongtao 458626816eSJia Hongtao static inline void _mpic_msgr_disable(struct mpic_msgr *msgr) 468626816eSJia Hongtao { 478626816eSJia Hongtao u32 mer = _mpic_msgr_mer_read(msgr); 488626816eSJia Hongtao 498626816eSJia Hongtao _mpic_msgr_mer_write(msgr, mer & ~(1 << msgr->num)); 508626816eSJia Hongtao } 518626816eSJia Hongtao 528626816eSJia Hongtao struct mpic_msgr *mpic_msgr_get(unsigned int reg_num) 538626816eSJia Hongtao { 548626816eSJia Hongtao unsigned long flags; 558626816eSJia Hongtao struct mpic_msgr *msgr; 568626816eSJia Hongtao 578626816eSJia Hongtao /* Assume busy until proven otherwise. */ 588626816eSJia Hongtao msgr = ERR_PTR(-EBUSY); 598626816eSJia Hongtao 608626816eSJia Hongtao if (reg_num >= mpic_msgr_count) 618626816eSJia Hongtao return ERR_PTR(-ENODEV); 628626816eSJia Hongtao 63e0a5a6c3SMingkai Hu raw_spin_lock_irqsave(&msgrs_lock, flags); 648626816eSJia Hongtao msgr = mpic_msgrs[reg_num]; 65e0a5a6c3SMingkai Hu if (msgr->in_use == MSGR_FREE) 668626816eSJia Hongtao msgr->in_use = MSGR_INUSE; 67e0a5a6c3SMingkai Hu raw_spin_unlock_irqrestore(&msgrs_lock, flags); 688626816eSJia Hongtao 698626816eSJia Hongtao return msgr; 708626816eSJia Hongtao } 718626816eSJia Hongtao EXPORT_SYMBOL_GPL(mpic_msgr_get); 728626816eSJia Hongtao 738626816eSJia Hongtao void mpic_msgr_put(struct mpic_msgr *msgr) 748626816eSJia Hongtao { 758626816eSJia Hongtao unsigned long flags; 768626816eSJia Hongtao 778626816eSJia Hongtao raw_spin_lock_irqsave(&msgr->lock, flags); 788626816eSJia Hongtao msgr->in_use = MSGR_FREE; 798626816eSJia Hongtao _mpic_msgr_disable(msgr); 808626816eSJia Hongtao raw_spin_unlock_irqrestore(&msgr->lock, flags); 818626816eSJia Hongtao } 828626816eSJia Hongtao EXPORT_SYMBOL_GPL(mpic_msgr_put); 838626816eSJia Hongtao 848626816eSJia Hongtao void mpic_msgr_enable(struct mpic_msgr *msgr) 858626816eSJia Hongtao { 868626816eSJia Hongtao unsigned long flags; 878626816eSJia Hongtao u32 mer; 888626816eSJia Hongtao 898626816eSJia Hongtao raw_spin_lock_irqsave(&msgr->lock, flags); 908626816eSJia Hongtao mer = _mpic_msgr_mer_read(msgr); 918626816eSJia Hongtao _mpic_msgr_mer_write(msgr, mer | (1 << msgr->num)); 928626816eSJia Hongtao raw_spin_unlock_irqrestore(&msgr->lock, flags); 938626816eSJia Hongtao } 948626816eSJia Hongtao EXPORT_SYMBOL_GPL(mpic_msgr_enable); 958626816eSJia Hongtao 968626816eSJia Hongtao void mpic_msgr_disable(struct mpic_msgr *msgr) 978626816eSJia Hongtao { 988626816eSJia Hongtao unsigned long flags; 998626816eSJia Hongtao 1008626816eSJia Hongtao raw_spin_lock_irqsave(&msgr->lock, flags); 1018626816eSJia Hongtao _mpic_msgr_disable(msgr); 1028626816eSJia Hongtao raw_spin_unlock_irqrestore(&msgr->lock, flags); 1038626816eSJia Hongtao } 1048626816eSJia Hongtao EXPORT_SYMBOL_GPL(mpic_msgr_disable); 1058626816eSJia Hongtao 1068626816eSJia Hongtao /* The following three functions are used to compute the order and number of 1078626816eSJia Hongtao * the message register blocks. They are clearly very inefficent. However, 1088626816eSJia Hongtao * they are called *only* a few times during device initialization. 1098626816eSJia Hongtao */ 1108626816eSJia Hongtao static unsigned int mpic_msgr_number_of_blocks(void) 1118626816eSJia Hongtao { 1128626816eSJia Hongtao unsigned int count; 1138626816eSJia Hongtao struct device_node *aliases; 1148626816eSJia Hongtao 1158626816eSJia Hongtao count = 0; 1168626816eSJia Hongtao aliases = of_find_node_by_name(NULL, "aliases"); 1178626816eSJia Hongtao 1188626816eSJia Hongtao if (aliases) { 1198626816eSJia Hongtao char buf[32]; 1208626816eSJia Hongtao 1218626816eSJia Hongtao for (;;) { 1228626816eSJia Hongtao snprintf(buf, sizeof(buf), "mpic-msgr-block%d", count); 1238626816eSJia Hongtao if (!of_find_property(aliases, buf, NULL)) 1248626816eSJia Hongtao break; 1258626816eSJia Hongtao 1268626816eSJia Hongtao count += 1; 1278626816eSJia Hongtao } 1288626816eSJia Hongtao } 1298626816eSJia Hongtao 1308626816eSJia Hongtao return count; 1318626816eSJia Hongtao } 1328626816eSJia Hongtao 1338626816eSJia Hongtao static unsigned int mpic_msgr_number_of_registers(void) 1348626816eSJia Hongtao { 1358626816eSJia Hongtao return mpic_msgr_number_of_blocks() * MPIC_MSGR_REGISTERS_PER_BLOCK; 1368626816eSJia Hongtao } 1378626816eSJia Hongtao 1388626816eSJia Hongtao static int mpic_msgr_block_number(struct device_node *node) 1398626816eSJia Hongtao { 1408626816eSJia Hongtao struct device_node *aliases; 1418626816eSJia Hongtao unsigned int index, number_of_blocks; 1428626816eSJia Hongtao char buf[64]; 1438626816eSJia Hongtao 1448626816eSJia Hongtao number_of_blocks = mpic_msgr_number_of_blocks(); 1458626816eSJia Hongtao aliases = of_find_node_by_name(NULL, "aliases"); 1468626816eSJia Hongtao if (!aliases) 1478626816eSJia Hongtao return -1; 1488626816eSJia Hongtao 1498626816eSJia Hongtao for (index = 0; index < number_of_blocks; ++index) { 1508626816eSJia Hongtao struct property *prop; 1518626816eSJia Hongtao 1528626816eSJia Hongtao snprintf(buf, sizeof(buf), "mpic-msgr-block%d", index); 1538626816eSJia Hongtao prop = of_find_property(aliases, buf, NULL); 1548626816eSJia Hongtao if (node == of_find_node_by_path(prop->value)) 1558626816eSJia Hongtao break; 1568626816eSJia Hongtao } 1578626816eSJia Hongtao 1588626816eSJia Hongtao return index == number_of_blocks ? -1 : index; 1598626816eSJia Hongtao } 1608626816eSJia Hongtao 1618626816eSJia Hongtao /* The probe function for a single message register block. 1628626816eSJia Hongtao */ 163cad5cef6SGreg Kroah-Hartman static int mpic_msgr_probe(struct platform_device *dev) 1648626816eSJia Hongtao { 1658626816eSJia Hongtao void __iomem *msgr_block_addr; 1668626816eSJia Hongtao int block_number; 1678626816eSJia Hongtao struct resource rsrc; 1688626816eSJia Hongtao unsigned int i; 1698626816eSJia Hongtao unsigned int irq_index; 1708626816eSJia Hongtao struct device_node *np = dev->dev.of_node; 1718626816eSJia Hongtao unsigned int receive_mask; 1728626816eSJia Hongtao const unsigned int *prop; 1738626816eSJia Hongtao 1748626816eSJia Hongtao if (!np) { 1758626816eSJia Hongtao dev_err(&dev->dev, "Device OF-Node is NULL"); 1768626816eSJia Hongtao return -EFAULT; 1778626816eSJia Hongtao } 1788626816eSJia Hongtao 1798626816eSJia Hongtao /* Allocate the message register array upon the first device 1808626816eSJia Hongtao * registered. 1818626816eSJia Hongtao */ 1828626816eSJia Hongtao if (!mpic_msgrs) { 1838626816eSJia Hongtao mpic_msgr_count = mpic_msgr_number_of_registers(); 1848626816eSJia Hongtao dev_info(&dev->dev, "Found %d message registers\n", 1858626816eSJia Hongtao mpic_msgr_count); 1868626816eSJia Hongtao 187983e2444SHimangi Saraogi mpic_msgrs = kcalloc(mpic_msgr_count, sizeof(*mpic_msgrs), 1888626816eSJia Hongtao GFP_KERNEL); 1898626816eSJia Hongtao if (!mpic_msgrs) { 1908626816eSJia Hongtao dev_err(&dev->dev, 1918626816eSJia Hongtao "No memory for message register blocks\n"); 1928626816eSJia Hongtao return -ENOMEM; 1938626816eSJia Hongtao } 1948626816eSJia Hongtao } 1958626816eSJia Hongtao dev_info(&dev->dev, "Of-device full name %s\n", np->full_name); 1968626816eSJia Hongtao 1978626816eSJia Hongtao /* IO map the message register block. */ 1988626816eSJia Hongtao of_address_to_resource(np, 0, &rsrc); 1998626816eSJia Hongtao msgr_block_addr = ioremap(rsrc.start, rsrc.end - rsrc.start); 2008626816eSJia Hongtao if (!msgr_block_addr) { 2018626816eSJia Hongtao dev_err(&dev->dev, "Failed to iomap MPIC message registers"); 2028626816eSJia Hongtao return -EFAULT; 2038626816eSJia Hongtao } 2048626816eSJia Hongtao 2058626816eSJia Hongtao /* Ensure the block has a defined order. */ 2068626816eSJia Hongtao block_number = mpic_msgr_block_number(np); 2078626816eSJia Hongtao if (block_number < 0) { 2088626816eSJia Hongtao dev_err(&dev->dev, 2098626816eSJia Hongtao "Failed to find message register block alias\n"); 2108626816eSJia Hongtao return -ENODEV; 2118626816eSJia Hongtao } 2128626816eSJia Hongtao dev_info(&dev->dev, "Setting up message register block %d\n", 2138626816eSJia Hongtao block_number); 2148626816eSJia Hongtao 2158626816eSJia Hongtao /* Grab the receive mask which specifies what registers can receive 2168626816eSJia Hongtao * interrupts. 2178626816eSJia Hongtao */ 2188626816eSJia Hongtao prop = of_get_property(np, "mpic-msgr-receive-mask", NULL); 2198626816eSJia Hongtao receive_mask = (prop) ? *prop : 0xF; 2208626816eSJia Hongtao 2218626816eSJia Hongtao /* Build up the appropriate message register data structures. */ 2228626816eSJia Hongtao for (i = 0, irq_index = 0; i < MPIC_MSGR_REGISTERS_PER_BLOCK; ++i) { 2238626816eSJia Hongtao struct mpic_msgr *msgr; 2248626816eSJia Hongtao unsigned int reg_number; 2258626816eSJia Hongtao 2268626816eSJia Hongtao msgr = kzalloc(sizeof(struct mpic_msgr), GFP_KERNEL); 2278626816eSJia Hongtao if (!msgr) { 2288626816eSJia Hongtao dev_err(&dev->dev, "No memory for message register\n"); 2298626816eSJia Hongtao return -ENOMEM; 2308626816eSJia Hongtao } 2318626816eSJia Hongtao 2328626816eSJia Hongtao reg_number = block_number * MPIC_MSGR_REGISTERS_PER_BLOCK + i; 2338626816eSJia Hongtao msgr->base = msgr_block_addr + i * MPIC_MSGR_STRIDE; 234dea58bd1SMingkai Hu msgr->mer = (u32 *)((u8 *)msgr->base + MPIC_MSGR_MER_OFFSET); 2358626816eSJia Hongtao msgr->in_use = MSGR_FREE; 2368626816eSJia Hongtao msgr->num = i; 2378626816eSJia Hongtao raw_spin_lock_init(&msgr->lock); 2388626816eSJia Hongtao 2398626816eSJia Hongtao if (receive_mask & (1 << i)) { 240f7578496SThierry Reding msgr->irq = irq_of_parse_and_map(np, irq_index); 241f7578496SThierry Reding if (msgr->irq == NO_IRQ) { 2428626816eSJia Hongtao dev_err(&dev->dev, 2438626816eSJia Hongtao "Missing interrupt specifier"); 2448626816eSJia Hongtao kfree(msgr); 2458626816eSJia Hongtao return -EFAULT; 2468626816eSJia Hongtao } 2478626816eSJia Hongtao irq_index += 1; 2488626816eSJia Hongtao } else { 2498626816eSJia Hongtao msgr->irq = NO_IRQ; 2508626816eSJia Hongtao } 2518626816eSJia Hongtao 2528626816eSJia Hongtao mpic_msgrs[reg_number] = msgr; 2538626816eSJia Hongtao mpic_msgr_disable(msgr); 2548626816eSJia Hongtao dev_info(&dev->dev, "Register %d initialized: irq %d\n", 2558626816eSJia Hongtao reg_number, msgr->irq); 2568626816eSJia Hongtao 2578626816eSJia Hongtao } 2588626816eSJia Hongtao 2598626816eSJia Hongtao return 0; 2608626816eSJia Hongtao } 2618626816eSJia Hongtao 2628626816eSJia Hongtao static const struct of_device_id mpic_msgr_ids[] = { 2638626816eSJia Hongtao { 2648626816eSJia Hongtao .compatible = "fsl,mpic-v3.1-msgr", 2658626816eSJia Hongtao .data = NULL, 2668626816eSJia Hongtao }, 2678626816eSJia Hongtao {} 2688626816eSJia Hongtao }; 2698626816eSJia Hongtao 2708626816eSJia Hongtao static struct platform_driver mpic_msgr_driver = { 2718626816eSJia Hongtao .driver = { 2728626816eSJia Hongtao .name = "mpic-msgr", 2738626816eSJia Hongtao .owner = THIS_MODULE, 2748626816eSJia Hongtao .of_match_table = mpic_msgr_ids, 2758626816eSJia Hongtao }, 2768626816eSJia Hongtao .probe = mpic_msgr_probe, 2778626816eSJia Hongtao }; 2788626816eSJia Hongtao 2798626816eSJia Hongtao static __init int mpic_msgr_init(void) 2808626816eSJia Hongtao { 2818626816eSJia Hongtao return platform_driver_register(&mpic_msgr_driver); 2828626816eSJia Hongtao } 2838626816eSJia Hongtao subsys_initcall(mpic_msgr_init); 284