285 lines · 6.8 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* * Copyright 2011-2012, Meador Inge, Mentor Graphics Corporation. * * Some ideas based on un-pushed work done by Vivek Mahajan, Jason Jin, and * Mingkai Hu from Freescale Semiconductor, Inc. */ #include <linux/list.h>#include <linux/of.h>#include <linux/of_address.h>#include <linux/of_irq.h>#include <linux/platform_device.h>#include <linux/errno.h>#include <linux/err.h>#include <linux/export.h>#include <linux/slab.h>#include <asm/hw_irq.h>#include <asm/ppc-pci.h>#include <asm/mpic_msgr.h> #define MPIC_MSGR_REGISTERS_PER_BLOCK	4#define MPIC_MSGR_STRIDE		0x10#define MPIC_MSGR_MER_OFFSET		(0x100 / sizeof(u32))#define MSGR_INUSE			0#define MSGR_FREE			1 static struct mpic_msgr **mpic_msgrs;static unsigned int mpic_msgr_count;static DEFINE_RAW_SPINLOCK(msgrs_lock); static inline void _mpic_msgr_mer_write(struct mpic_msgr *msgr, u32 value){	out_be32(msgr->mer, value);} static inline u32 _mpic_msgr_mer_read(struct mpic_msgr *msgr){	return in_be32(msgr->mer);} static inline void _mpic_msgr_disable(struct mpic_msgr *msgr){	u32 mer = _mpic_msgr_mer_read(msgr); 	_mpic_msgr_mer_write(msgr, mer & ~(1 << msgr->num));} struct mpic_msgr *mpic_msgr_get(unsigned int reg_num){	unsigned long flags;	struct mpic_msgr *msgr; 	/* Assume busy until proven otherwise.  */	msgr = ERR_PTR(-EBUSY); 	if (reg_num >= mpic_msgr_count)		return ERR_PTR(-ENODEV); 	raw_spin_lock_irqsave(&msgrs_lock, flags);	msgr = mpic_msgrs[reg_num];	if (msgr->in_use == MSGR_FREE)		msgr->in_use = MSGR_INUSE;	raw_spin_unlock_irqrestore(&msgrs_lock, flags); 	return msgr;}EXPORT_SYMBOL_GPL(mpic_msgr_get); void mpic_msgr_put(struct mpic_msgr *msgr){	unsigned long flags; 	raw_spin_lock_irqsave(&msgr->lock, flags);	msgr->in_use = MSGR_FREE;	_mpic_msgr_disable(msgr);	raw_spin_unlock_irqrestore(&msgr->lock, flags);}EXPORT_SYMBOL_GPL(mpic_msgr_put); void mpic_msgr_enable(struct mpic_msgr *msgr){	unsigned long flags;	u32 mer; 	raw_spin_lock_irqsave(&msgr->lock, flags);	mer = _mpic_msgr_mer_read(msgr);	_mpic_msgr_mer_write(msgr, mer | (1 << msgr->num));	raw_spin_unlock_irqrestore(&msgr->lock, flags);}EXPORT_SYMBOL_GPL(mpic_msgr_enable); void mpic_msgr_disable(struct mpic_msgr *msgr){	unsigned long flags; 	raw_spin_lock_irqsave(&msgr->lock, flags);	_mpic_msgr_disable(msgr);	raw_spin_unlock_irqrestore(&msgr->lock, flags);}EXPORT_SYMBOL_GPL(mpic_msgr_disable); /* The following three functions are used to compute the order and number of * the message register blocks.  They are clearly very inefficient.  However, * they are called *only* a few times during device initialization. */static unsigned int mpic_msgr_number_of_blocks(void){	unsigned int count;	struct device_node *aliases; 	count = 0;	aliases = of_find_node_by_name(NULL, "aliases"); 	if (aliases) {		char buf[32]; 		for (;;) {			snprintf(buf, sizeof(buf), "mpic-msgr-block%d", count);			if (!of_property_present(aliases, buf))				break; 			count += 1;		}		of_node_put(aliases);	} 	return count;} static unsigned int mpic_msgr_number_of_registers(void){	return mpic_msgr_number_of_blocks() * MPIC_MSGR_REGISTERS_PER_BLOCK;} static int mpic_msgr_block_number(struct device_node *node){	struct device_node *aliases;	unsigned int index, number_of_blocks;	char buf[64]; 	number_of_blocks = mpic_msgr_number_of_blocks();	aliases = of_find_node_by_name(NULL, "aliases");	if (!aliases)		return -1; 	for (index = 0; index < number_of_blocks; ++index) {		struct property *prop;		struct device_node *tn; 		snprintf(buf, sizeof(buf), "mpic-msgr-block%d", index);		prop = of_find_property(aliases, buf, NULL);		tn = of_find_node_by_path(prop->value);		if (node == tn) {			of_node_put(tn);			break;		}		of_node_put(tn);	}	of_node_put(aliases); 	return index == number_of_blocks ? -1 : index;} /* The probe function for a single message register block. */static int mpic_msgr_probe(struct platform_device *dev){	void __iomem *msgr_block_addr;	int block_number;	struct resource rsrc;	unsigned int i;	unsigned int irq_index;	struct device_node *np = dev->dev.of_node;	unsigned int receive_mask;	const unsigned int *prop; 	if (!np) {		dev_err(&dev->dev, "Device OF-Node is NULL");		return -EFAULT;	} 	/* Allocate the message register array upon the first device	 * registered.	 */	if (!mpic_msgrs) {		mpic_msgr_count = mpic_msgr_number_of_registers();		dev_info(&dev->dev, "Found %d message registers\n",				mpic_msgr_count); 		mpic_msgrs = kzalloc_objs(*mpic_msgrs, mpic_msgr_count);		if (!mpic_msgrs) {			dev_err(&dev->dev,				"No memory for message register blocks\n");			return -ENOMEM;		}	}	dev_info(&dev->dev, "Of-device full name %pOF\n", np); 	/* IO map the message register block. */	of_address_to_resource(np, 0, &rsrc);	msgr_block_addr = devm_ioremap(&dev->dev, rsrc.start, resource_size(&rsrc));	if (!msgr_block_addr) {		dev_err(&dev->dev, "Failed to iomap MPIC message registers");		return -EFAULT;	} 	/* Ensure the block has a defined order. */	block_number = mpic_msgr_block_number(np);	if (block_number < 0) {		dev_err(&dev->dev,			"Failed to find message register block alias\n");		return -ENODEV;	}	dev_info(&dev->dev, "Setting up message register block %d\n",			block_number); 	/* Grab the receive mask which specifies what registers can receive	 * interrupts.	 */	prop = of_get_property(np, "mpic-msgr-receive-mask", NULL);	receive_mask = (prop) ? *prop : 0xF; 	/* Build up the appropriate message register data structures. */	for (i = 0, irq_index = 0; i < MPIC_MSGR_REGISTERS_PER_BLOCK; ++i) {		struct mpic_msgr *msgr;		unsigned int reg_number; 		msgr = kzalloc_obj(struct mpic_msgr);		if (!msgr) {			dev_err(&dev->dev, "No memory for message register\n");			return -ENOMEM;		} 		reg_number = block_number * MPIC_MSGR_REGISTERS_PER_BLOCK + i;		msgr->base = msgr_block_addr + i * MPIC_MSGR_STRIDE;		msgr->mer = msgr->base + MPIC_MSGR_MER_OFFSET;		msgr->in_use = MSGR_FREE;		msgr->num = i;		raw_spin_lock_init(&msgr->lock); 		if (receive_mask & (1 << i)) {			msgr->irq = irq_of_parse_and_map(np, irq_index);			if (!msgr->irq) {				dev_err(&dev->dev,						"Missing interrupt specifier");				kfree(msgr);				return -EFAULT;			}			irq_index += 1;		} else {			msgr->irq = 0;		} 		mpic_msgrs[reg_number] = msgr;		mpic_msgr_disable(msgr);		dev_info(&dev->dev, "Register %d initialized: irq %d\n",				reg_number, msgr->irq); 	} 	return 0;} static const struct of_device_id mpic_msgr_ids[] = {	{		.compatible = "fsl,mpic-v3.1-msgr",		.data = NULL,	},	{}}; static struct platform_driver mpic_msgr_driver = {	.driver = {		.name = "mpic-msgr",		.of_match_table = mpic_msgr_ids,	},	.probe = mpic_msgr_probe,}; static __init int mpic_msgr_init(void){	return platform_driver_register(&mpic_msgr_driver);}subsys_initcall(mpic_msgr_init);