162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright 2011-2012, Meador Inge, Mentor Graphics Corporation. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Some ideas based on un-pushed work done by Vivek Mahajan, Jason Jin, and 662306a36Sopenharmony_ci * Mingkai Hu from Freescale Semiconductor, Inc. 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/list.h> 1062306a36Sopenharmony_ci#include <linux/of.h> 1162306a36Sopenharmony_ci#include <linux/of_address.h> 1262306a36Sopenharmony_ci#include <linux/of_irq.h> 1362306a36Sopenharmony_ci#include <linux/platform_device.h> 1462306a36Sopenharmony_ci#include <linux/errno.h> 1562306a36Sopenharmony_ci#include <linux/err.h> 1662306a36Sopenharmony_ci#include <linux/export.h> 1762306a36Sopenharmony_ci#include <linux/slab.h> 1862306a36Sopenharmony_ci#include <asm/hw_irq.h> 1962306a36Sopenharmony_ci#include <asm/ppc-pci.h> 2062306a36Sopenharmony_ci#include <asm/mpic_msgr.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define MPIC_MSGR_REGISTERS_PER_BLOCK 4 2362306a36Sopenharmony_ci#define MPIC_MSGR_STRIDE 0x10 2462306a36Sopenharmony_ci#define MPIC_MSGR_MER_OFFSET (0x100 / sizeof(u32)) 2562306a36Sopenharmony_ci#define MSGR_INUSE 0 2662306a36Sopenharmony_ci#define MSGR_FREE 1 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic struct mpic_msgr **mpic_msgrs; 2962306a36Sopenharmony_cistatic unsigned int mpic_msgr_count; 3062306a36Sopenharmony_cistatic DEFINE_RAW_SPINLOCK(msgrs_lock); 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistatic inline void _mpic_msgr_mer_write(struct mpic_msgr *msgr, u32 value) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci out_be32(msgr->mer, value); 3562306a36Sopenharmony_ci} 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic inline u32 _mpic_msgr_mer_read(struct mpic_msgr *msgr) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci return in_be32(msgr->mer); 4062306a36Sopenharmony_ci} 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic inline void _mpic_msgr_disable(struct mpic_msgr *msgr) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci u32 mer = _mpic_msgr_mer_read(msgr); 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci _mpic_msgr_mer_write(msgr, mer & ~(1 << msgr->num)); 4762306a36Sopenharmony_ci} 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistruct mpic_msgr *mpic_msgr_get(unsigned int reg_num) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci unsigned long flags; 5262306a36Sopenharmony_ci struct mpic_msgr *msgr; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci /* Assume busy until proven otherwise. */ 5562306a36Sopenharmony_ci msgr = ERR_PTR(-EBUSY); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci if (reg_num >= mpic_msgr_count) 5862306a36Sopenharmony_ci return ERR_PTR(-ENODEV); 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci raw_spin_lock_irqsave(&msgrs_lock, flags); 6162306a36Sopenharmony_ci msgr = mpic_msgrs[reg_num]; 6262306a36Sopenharmony_ci if (msgr->in_use == MSGR_FREE) 6362306a36Sopenharmony_ci msgr->in_use = MSGR_INUSE; 6462306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&msgrs_lock, flags); 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci return msgr; 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mpic_msgr_get); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_civoid mpic_msgr_put(struct mpic_msgr *msgr) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci unsigned long flags; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci raw_spin_lock_irqsave(&msgr->lock, flags); 7562306a36Sopenharmony_ci msgr->in_use = MSGR_FREE; 7662306a36Sopenharmony_ci _mpic_msgr_disable(msgr); 7762306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&msgr->lock, flags); 7862306a36Sopenharmony_ci} 7962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mpic_msgr_put); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_civoid mpic_msgr_enable(struct mpic_msgr *msgr) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci unsigned long flags; 8462306a36Sopenharmony_ci u32 mer; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci raw_spin_lock_irqsave(&msgr->lock, flags); 8762306a36Sopenharmony_ci mer = _mpic_msgr_mer_read(msgr); 8862306a36Sopenharmony_ci _mpic_msgr_mer_write(msgr, mer | (1 << msgr->num)); 8962306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&msgr->lock, flags); 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mpic_msgr_enable); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_civoid mpic_msgr_disable(struct mpic_msgr *msgr) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci unsigned long flags; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci raw_spin_lock_irqsave(&msgr->lock, flags); 9862306a36Sopenharmony_ci _mpic_msgr_disable(msgr); 9962306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&msgr->lock, flags); 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mpic_msgr_disable); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci/* The following three functions are used to compute the order and number of 10462306a36Sopenharmony_ci * the message register blocks. They are clearly very inefficient. However, 10562306a36Sopenharmony_ci * they are called *only* a few times during device initialization. 10662306a36Sopenharmony_ci */ 10762306a36Sopenharmony_cistatic unsigned int mpic_msgr_number_of_blocks(void) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci unsigned int count; 11062306a36Sopenharmony_ci struct device_node *aliases; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci count = 0; 11362306a36Sopenharmony_ci aliases = of_find_node_by_name(NULL, "aliases"); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci if (aliases) { 11662306a36Sopenharmony_ci char buf[32]; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci for (;;) { 11962306a36Sopenharmony_ci snprintf(buf, sizeof(buf), "mpic-msgr-block%d", count); 12062306a36Sopenharmony_ci if (!of_property_present(aliases, buf)) 12162306a36Sopenharmony_ci break; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci count += 1; 12462306a36Sopenharmony_ci } 12562306a36Sopenharmony_ci of_node_put(aliases); 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci return count; 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistatic unsigned int mpic_msgr_number_of_registers(void) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci return mpic_msgr_number_of_blocks() * MPIC_MSGR_REGISTERS_PER_BLOCK; 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistatic int mpic_msgr_block_number(struct device_node *node) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci struct device_node *aliases; 13962306a36Sopenharmony_ci unsigned int index, number_of_blocks; 14062306a36Sopenharmony_ci char buf[64]; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci number_of_blocks = mpic_msgr_number_of_blocks(); 14362306a36Sopenharmony_ci aliases = of_find_node_by_name(NULL, "aliases"); 14462306a36Sopenharmony_ci if (!aliases) 14562306a36Sopenharmony_ci return -1; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci for (index = 0; index < number_of_blocks; ++index) { 14862306a36Sopenharmony_ci struct property *prop; 14962306a36Sopenharmony_ci struct device_node *tn; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci snprintf(buf, sizeof(buf), "mpic-msgr-block%d", index); 15262306a36Sopenharmony_ci prop = of_find_property(aliases, buf, NULL); 15362306a36Sopenharmony_ci tn = of_find_node_by_path(prop->value); 15462306a36Sopenharmony_ci if (node == tn) { 15562306a36Sopenharmony_ci of_node_put(tn); 15662306a36Sopenharmony_ci break; 15762306a36Sopenharmony_ci } 15862306a36Sopenharmony_ci of_node_put(tn); 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci of_node_put(aliases); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci return index == number_of_blocks ? -1 : index; 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci/* The probe function for a single message register block. 16662306a36Sopenharmony_ci */ 16762306a36Sopenharmony_cistatic int mpic_msgr_probe(struct platform_device *dev) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci void __iomem *msgr_block_addr; 17062306a36Sopenharmony_ci int block_number; 17162306a36Sopenharmony_ci struct resource rsrc; 17262306a36Sopenharmony_ci unsigned int i; 17362306a36Sopenharmony_ci unsigned int irq_index; 17462306a36Sopenharmony_ci struct device_node *np = dev->dev.of_node; 17562306a36Sopenharmony_ci unsigned int receive_mask; 17662306a36Sopenharmony_ci const unsigned int *prop; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci if (!np) { 17962306a36Sopenharmony_ci dev_err(&dev->dev, "Device OF-Node is NULL"); 18062306a36Sopenharmony_ci return -EFAULT; 18162306a36Sopenharmony_ci } 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci /* Allocate the message register array upon the first device 18462306a36Sopenharmony_ci * registered. 18562306a36Sopenharmony_ci */ 18662306a36Sopenharmony_ci if (!mpic_msgrs) { 18762306a36Sopenharmony_ci mpic_msgr_count = mpic_msgr_number_of_registers(); 18862306a36Sopenharmony_ci dev_info(&dev->dev, "Found %d message registers\n", 18962306a36Sopenharmony_ci mpic_msgr_count); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci mpic_msgrs = kcalloc(mpic_msgr_count, sizeof(*mpic_msgrs), 19262306a36Sopenharmony_ci GFP_KERNEL); 19362306a36Sopenharmony_ci if (!mpic_msgrs) { 19462306a36Sopenharmony_ci dev_err(&dev->dev, 19562306a36Sopenharmony_ci "No memory for message register blocks\n"); 19662306a36Sopenharmony_ci return -ENOMEM; 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci } 19962306a36Sopenharmony_ci dev_info(&dev->dev, "Of-device full name %pOF\n", np); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci /* IO map the message register block. */ 20262306a36Sopenharmony_ci of_address_to_resource(np, 0, &rsrc); 20362306a36Sopenharmony_ci msgr_block_addr = devm_ioremap(&dev->dev, rsrc.start, resource_size(&rsrc)); 20462306a36Sopenharmony_ci if (!msgr_block_addr) { 20562306a36Sopenharmony_ci dev_err(&dev->dev, "Failed to iomap MPIC message registers"); 20662306a36Sopenharmony_ci return -EFAULT; 20762306a36Sopenharmony_ci } 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci /* Ensure the block has a defined order. */ 21062306a36Sopenharmony_ci block_number = mpic_msgr_block_number(np); 21162306a36Sopenharmony_ci if (block_number < 0) { 21262306a36Sopenharmony_ci dev_err(&dev->dev, 21362306a36Sopenharmony_ci "Failed to find message register block alias\n"); 21462306a36Sopenharmony_ci return -ENODEV; 21562306a36Sopenharmony_ci } 21662306a36Sopenharmony_ci dev_info(&dev->dev, "Setting up message register block %d\n", 21762306a36Sopenharmony_ci block_number); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci /* Grab the receive mask which specifies what registers can receive 22062306a36Sopenharmony_ci * interrupts. 22162306a36Sopenharmony_ci */ 22262306a36Sopenharmony_ci prop = of_get_property(np, "mpic-msgr-receive-mask", NULL); 22362306a36Sopenharmony_ci receive_mask = (prop) ? *prop : 0xF; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci /* Build up the appropriate message register data structures. */ 22662306a36Sopenharmony_ci for (i = 0, irq_index = 0; i < MPIC_MSGR_REGISTERS_PER_BLOCK; ++i) { 22762306a36Sopenharmony_ci struct mpic_msgr *msgr; 22862306a36Sopenharmony_ci unsigned int reg_number; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci msgr = kzalloc(sizeof(struct mpic_msgr), GFP_KERNEL); 23162306a36Sopenharmony_ci if (!msgr) { 23262306a36Sopenharmony_ci dev_err(&dev->dev, "No memory for message register\n"); 23362306a36Sopenharmony_ci return -ENOMEM; 23462306a36Sopenharmony_ci } 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci reg_number = block_number * MPIC_MSGR_REGISTERS_PER_BLOCK + i; 23762306a36Sopenharmony_ci msgr->base = msgr_block_addr + i * MPIC_MSGR_STRIDE; 23862306a36Sopenharmony_ci msgr->mer = msgr->base + MPIC_MSGR_MER_OFFSET; 23962306a36Sopenharmony_ci msgr->in_use = MSGR_FREE; 24062306a36Sopenharmony_ci msgr->num = i; 24162306a36Sopenharmony_ci raw_spin_lock_init(&msgr->lock); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci if (receive_mask & (1 << i)) { 24462306a36Sopenharmony_ci msgr->irq = irq_of_parse_and_map(np, irq_index); 24562306a36Sopenharmony_ci if (!msgr->irq) { 24662306a36Sopenharmony_ci dev_err(&dev->dev, 24762306a36Sopenharmony_ci "Missing interrupt specifier"); 24862306a36Sopenharmony_ci kfree(msgr); 24962306a36Sopenharmony_ci return -EFAULT; 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci irq_index += 1; 25262306a36Sopenharmony_ci } else { 25362306a36Sopenharmony_ci msgr->irq = 0; 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci mpic_msgrs[reg_number] = msgr; 25762306a36Sopenharmony_ci mpic_msgr_disable(msgr); 25862306a36Sopenharmony_ci dev_info(&dev->dev, "Register %d initialized: irq %d\n", 25962306a36Sopenharmony_ci reg_number, msgr->irq); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci return 0; 26462306a36Sopenharmony_ci} 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_cistatic const struct of_device_id mpic_msgr_ids[] = { 26762306a36Sopenharmony_ci { 26862306a36Sopenharmony_ci .compatible = "fsl,mpic-v3.1-msgr", 26962306a36Sopenharmony_ci .data = NULL, 27062306a36Sopenharmony_ci }, 27162306a36Sopenharmony_ci {} 27262306a36Sopenharmony_ci}; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_cistatic struct platform_driver mpic_msgr_driver = { 27562306a36Sopenharmony_ci .driver = { 27662306a36Sopenharmony_ci .name = "mpic-msgr", 27762306a36Sopenharmony_ci .of_match_table = mpic_msgr_ids, 27862306a36Sopenharmony_ci }, 27962306a36Sopenharmony_ci .probe = mpic_msgr_probe, 28062306a36Sopenharmony_ci}; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_cistatic __init int mpic_msgr_init(void) 28362306a36Sopenharmony_ci{ 28462306a36Sopenharmony_ci return platform_driver_register(&mpic_msgr_driver); 28562306a36Sopenharmony_ci} 28662306a36Sopenharmony_cisubsys_initcall(mpic_msgr_init); 287