| /* | 
 |  * This program is free software; you can redistribute it and/or modify | 
 |  * it under the terms of the GNU General Public License version 2 as | 
 |  * published by the Free Software Foundation. | 
 |  * | 
 |  * This program is distributed in the hope that it will be useful, | 
 |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
 |  * GNU General Public License for more details. | 
 |  * | 
 |  * Copyright (C) 2012 ARM Limited | 
 |  */ | 
 |  | 
 | #define pr_fmt(fmt) "vexpress-config: " fmt | 
 |  | 
 | #include <linux/bitops.h> | 
 | #include <linux/completion.h> | 
 | #include <linux/export.h> | 
 | #include <linux/list.h> | 
 | #include <linux/of.h> | 
 | #include <linux/of_device.h> | 
 | #include <linux/slab.h> | 
 | #include <linux/string.h> | 
 | #include <linux/vexpress.h> | 
 |  | 
 |  | 
 | #define VEXPRESS_CONFIG_MAX_BRIDGES 2 | 
 |  | 
 | static struct vexpress_config_bridge { | 
 | 	struct device_node *node; | 
 | 	struct vexpress_config_bridge_info *info; | 
 | 	struct list_head transactions; | 
 | 	spinlock_t transactions_lock; | 
 | } vexpress_config_bridges[VEXPRESS_CONFIG_MAX_BRIDGES]; | 
 |  | 
 | static DECLARE_BITMAP(vexpress_config_bridges_map, | 
 | 		ARRAY_SIZE(vexpress_config_bridges)); | 
 | static DEFINE_MUTEX(vexpress_config_bridges_mutex); | 
 |  | 
 | struct vexpress_config_bridge *vexpress_config_bridge_register( | 
 | 		struct device_node *node, | 
 | 		struct vexpress_config_bridge_info *info) | 
 | { | 
 | 	struct vexpress_config_bridge *bridge; | 
 | 	int i; | 
 |  | 
 | 	pr_debug("Registering bridge '%s'\n", info->name); | 
 |  | 
 | 	mutex_lock(&vexpress_config_bridges_mutex); | 
 | 	i = find_first_zero_bit(vexpress_config_bridges_map, | 
 | 			ARRAY_SIZE(vexpress_config_bridges)); | 
 | 	if (i >= ARRAY_SIZE(vexpress_config_bridges)) { | 
 | 		pr_err("Can't register more bridges!\n"); | 
 | 		mutex_unlock(&vexpress_config_bridges_mutex); | 
 | 		return NULL; | 
 | 	} | 
 | 	__set_bit(i, vexpress_config_bridges_map); | 
 | 	bridge = &vexpress_config_bridges[i]; | 
 |  | 
 | 	bridge->node = node; | 
 | 	bridge->info = info; | 
 | 	INIT_LIST_HEAD(&bridge->transactions); | 
 | 	spin_lock_init(&bridge->transactions_lock); | 
 |  | 
 | 	mutex_unlock(&vexpress_config_bridges_mutex); | 
 |  | 
 | 	return bridge; | 
 | } | 
 | EXPORT_SYMBOL(vexpress_config_bridge_register); | 
 |  | 
 | void vexpress_config_bridge_unregister(struct vexpress_config_bridge *bridge) | 
 | { | 
 | 	struct vexpress_config_bridge __bridge = *bridge; | 
 | 	int i; | 
 |  | 
 | 	mutex_lock(&vexpress_config_bridges_mutex); | 
 | 	for (i = 0; i < ARRAY_SIZE(vexpress_config_bridges); i++) | 
 | 		if (&vexpress_config_bridges[i] == bridge) | 
 | 			__clear_bit(i, vexpress_config_bridges_map); | 
 | 	mutex_unlock(&vexpress_config_bridges_mutex); | 
 |  | 
 | 	WARN_ON(!list_empty(&__bridge.transactions)); | 
 | 	while (!list_empty(&__bridge.transactions)) | 
 | 		cpu_relax(); | 
 | } | 
 | EXPORT_SYMBOL(vexpress_config_bridge_unregister); | 
 |  | 
 |  | 
 | struct vexpress_config_func { | 
 | 	struct vexpress_config_bridge *bridge; | 
 | 	void *func; | 
 | }; | 
 |  | 
 | struct vexpress_config_func *__vexpress_config_func_get(struct device *dev, | 
 | 		struct device_node *node) | 
 | { | 
 | 	struct device_node *bridge_node; | 
 | 	struct vexpress_config_func *func; | 
 | 	int i; | 
 |  | 
 | 	if (WARN_ON(dev && node && dev->of_node != node)) | 
 | 		return NULL; | 
 | 	if (dev && !node) | 
 | 		node = dev->of_node; | 
 |  | 
 | 	func = kzalloc(sizeof(*func), GFP_KERNEL); | 
 | 	if (!func) | 
 | 		return NULL; | 
 |  | 
 | 	bridge_node = of_node_get(node); | 
 | 	while (bridge_node) { | 
 | 		const __be32 *prop = of_get_property(bridge_node, | 
 | 				"arm,vexpress,config-bridge", NULL); | 
 |  | 
 | 		if (prop) { | 
 | 			bridge_node = of_find_node_by_phandle( | 
 | 					be32_to_cpup(prop)); | 
 | 			break; | 
 | 		} | 
 |  | 
 | 		bridge_node = of_get_next_parent(bridge_node); | 
 | 	} | 
 |  | 
 | 	mutex_lock(&vexpress_config_bridges_mutex); | 
 | 	for (i = 0; i < ARRAY_SIZE(vexpress_config_bridges); i++) { | 
 | 		struct vexpress_config_bridge *bridge = | 
 | 				&vexpress_config_bridges[i]; | 
 |  | 
 | 		if (test_bit(i, vexpress_config_bridges_map) && | 
 | 				bridge->node == bridge_node) { | 
 | 			func->bridge = bridge; | 
 | 			func->func = bridge->info->func_get(dev, node); | 
 | 			break; | 
 | 		} | 
 | 	} | 
 | 	mutex_unlock(&vexpress_config_bridges_mutex); | 
 |  | 
 | 	if (!func->func) { | 
 | 		of_node_put(node); | 
 | 		kfree(func); | 
 | 		return NULL; | 
 | 	} | 
 |  | 
 | 	return func; | 
 | } | 
 | EXPORT_SYMBOL(__vexpress_config_func_get); | 
 |  | 
 | void vexpress_config_func_put(struct vexpress_config_func *func) | 
 | { | 
 | 	func->bridge->info->func_put(func->func); | 
 | 	of_node_put(func->bridge->node); | 
 | 	kfree(func); | 
 | } | 
 | EXPORT_SYMBOL(vexpress_config_func_put); | 
 |  | 
 | struct vexpress_config_trans { | 
 | 	struct vexpress_config_func *func; | 
 | 	int offset; | 
 | 	bool write; | 
 | 	u32 *data; | 
 | 	int status; | 
 | 	struct completion completion; | 
 | 	struct list_head list; | 
 | }; | 
 |  | 
 | static void vexpress_config_dump_trans(const char *what, | 
 | 		struct vexpress_config_trans *trans) | 
 | { | 
 | 	pr_debug("%s %s trans %p func 0x%p offset %d data 0x%x status %d\n", | 
 | 			what, trans->write ? "write" : "read", trans, | 
 | 			trans->func->func, trans->offset, | 
 | 			trans->data ? *trans->data : 0, trans->status); | 
 | } | 
 |  | 
 | static int vexpress_config_schedule(struct vexpress_config_trans *trans) | 
 | { | 
 | 	int status; | 
 | 	struct vexpress_config_bridge *bridge = trans->func->bridge; | 
 | 	unsigned long flags; | 
 |  | 
 | 	init_completion(&trans->completion); | 
 | 	trans->status = -EFAULT; | 
 |  | 
 | 	spin_lock_irqsave(&bridge->transactions_lock, flags); | 
 |  | 
 | 	if (list_empty(&bridge->transactions)) { | 
 | 		vexpress_config_dump_trans("Executing", trans); | 
 | 		status = bridge->info->func_exec(trans->func->func, | 
 | 				trans->offset, trans->write, trans->data); | 
 | 	} else { | 
 | 		vexpress_config_dump_trans("Queuing", trans); | 
 | 		status = VEXPRESS_CONFIG_STATUS_WAIT; | 
 | 	} | 
 |  | 
 | 	switch (status) { | 
 | 	case VEXPRESS_CONFIG_STATUS_DONE: | 
 | 		vexpress_config_dump_trans("Finished", trans); | 
 | 		trans->status = status; | 
 | 		break; | 
 | 	case VEXPRESS_CONFIG_STATUS_WAIT: | 
 | 		list_add_tail(&trans->list, &bridge->transactions); | 
 | 		break; | 
 | 	} | 
 |  | 
 | 	spin_unlock_irqrestore(&bridge->transactions_lock, flags); | 
 |  | 
 | 	return status; | 
 | } | 
 |  | 
 | void vexpress_config_complete(struct vexpress_config_bridge *bridge, | 
 | 		int status) | 
 | { | 
 | 	struct vexpress_config_trans *trans; | 
 | 	unsigned long flags; | 
 | 	const char *message = "Completed"; | 
 |  | 
 | 	spin_lock_irqsave(&bridge->transactions_lock, flags); | 
 |  | 
 | 	trans = list_first_entry(&bridge->transactions, | 
 | 			struct vexpress_config_trans, list); | 
 | 	trans->status = status; | 
 |  | 
 | 	do { | 
 | 		vexpress_config_dump_trans(message, trans); | 
 | 		list_del(&trans->list); | 
 | 		complete(&trans->completion); | 
 |  | 
 | 		if (list_empty(&bridge->transactions)) | 
 | 			break; | 
 |  | 
 | 		trans = list_first_entry(&bridge->transactions, | 
 | 				struct vexpress_config_trans, list); | 
 | 		vexpress_config_dump_trans("Executing pending", trans); | 
 | 		trans->status = bridge->info->func_exec(trans->func->func, | 
 | 				trans->offset, trans->write, trans->data); | 
 | 		message = "Finished pending"; | 
 | 	} while (trans->status == VEXPRESS_CONFIG_STATUS_DONE); | 
 |  | 
 | 	spin_unlock_irqrestore(&bridge->transactions_lock, flags); | 
 | } | 
 | EXPORT_SYMBOL(vexpress_config_complete); | 
 |  | 
 | int vexpress_config_wait(struct vexpress_config_trans *trans) | 
 | { | 
 | 	wait_for_completion(&trans->completion); | 
 |  | 
 | 	return trans->status; | 
 | } | 
 | EXPORT_SYMBOL(vexpress_config_wait); | 
 |  | 
 | int vexpress_config_read(struct vexpress_config_func *func, int offset, | 
 | 		u32 *data) | 
 | { | 
 | 	struct vexpress_config_trans trans = { | 
 | 		.func = func, | 
 | 		.offset = offset, | 
 | 		.write = false, | 
 | 		.data = data, | 
 | 		.status = 0, | 
 | 	}; | 
 | 	int status = vexpress_config_schedule(&trans); | 
 |  | 
 | 	if (status == VEXPRESS_CONFIG_STATUS_WAIT) | 
 | 		status = vexpress_config_wait(&trans); | 
 |  | 
 | 	return status; | 
 | } | 
 | EXPORT_SYMBOL(vexpress_config_read); | 
 |  | 
 | int vexpress_config_write(struct vexpress_config_func *func, int offset, | 
 | 		u32 data) | 
 | { | 
 | 	struct vexpress_config_trans trans = { | 
 | 		.func = func, | 
 | 		.offset = offset, | 
 | 		.write = true, | 
 | 		.data = &data, | 
 | 		.status = 0, | 
 | 	}; | 
 | 	int status = vexpress_config_schedule(&trans); | 
 |  | 
 | 	if (status == VEXPRESS_CONFIG_STATUS_WAIT) | 
 | 		status = vexpress_config_wait(&trans); | 
 |  | 
 | 	return status; | 
 | } | 
 | EXPORT_SYMBOL(vexpress_config_write); |