145d9ca49SDean Nelson /* 245d9ca49SDean Nelson * This file is subject to the terms and conditions of the GNU General Public 345d9ca49SDean Nelson * License. See the file "COPYING" in the main directory of this archive 445d9ca49SDean Nelson * for more details. 545d9ca49SDean Nelson * 645d9ca49SDean Nelson * Copyright (c) 2004-2008 Silicon Graphics, Inc. All Rights Reserved. 745d9ca49SDean Nelson */ 845d9ca49SDean Nelson 945d9ca49SDean Nelson 1045d9ca49SDean Nelson /* 1145d9ca49SDean Nelson * Cross Partition (XP) base. 1245d9ca49SDean Nelson * 1345d9ca49SDean Nelson * XP provides a base from which its users can interact 1445d9ca49SDean Nelson * with XPC, yet not be dependent on XPC. 1545d9ca49SDean Nelson * 1645d9ca49SDean Nelson */ 1745d9ca49SDean Nelson 1845d9ca49SDean Nelson 1945d9ca49SDean Nelson #include <linux/kernel.h> 2045d9ca49SDean Nelson #include <linux/interrupt.h> 2145d9ca49SDean Nelson #include <linux/module.h> 2245d9ca49SDean Nelson #include <linux/mutex.h> 2345d9ca49SDean Nelson #include <asm/sn/intr.h> 2445d9ca49SDean Nelson #include <asm/sn/sn_sal.h> 2545d9ca49SDean Nelson #include "xp.h" 2645d9ca49SDean Nelson 2745d9ca49SDean Nelson 2845d9ca49SDean Nelson /* 2945d9ca49SDean Nelson * Target of nofault PIO read. 3045d9ca49SDean Nelson */ 3145d9ca49SDean Nelson u64 xp_nofault_PIOR_target; 3245d9ca49SDean Nelson 3345d9ca49SDean Nelson 3445d9ca49SDean Nelson /* 3545d9ca49SDean Nelson * xpc_registrations[] keeps track of xpc_connect()'s done by the kernel-level 3645d9ca49SDean Nelson * users of XPC. 3745d9ca49SDean Nelson */ 3845d9ca49SDean Nelson struct xpc_registration xpc_registrations[XPC_NCHANNELS]; 3945d9ca49SDean Nelson 4045d9ca49SDean Nelson 4145d9ca49SDean Nelson /* 4245d9ca49SDean Nelson * Initialize the XPC interface to indicate that XPC isn't loaded. 4345d9ca49SDean Nelson */ 4445d9ca49SDean Nelson static enum xpc_retval xpc_notloaded(void) { return xpcNotLoaded; } 4545d9ca49SDean Nelson 4645d9ca49SDean Nelson struct xpc_interface xpc_interface = { 4745d9ca49SDean Nelson (void (*)(int)) xpc_notloaded, 4845d9ca49SDean Nelson (void (*)(int)) xpc_notloaded, 4945d9ca49SDean Nelson (enum xpc_retval (*)(partid_t, int, u32, void **)) xpc_notloaded, 5045d9ca49SDean Nelson (enum xpc_retval (*)(partid_t, int, void *)) xpc_notloaded, 5145d9ca49SDean Nelson (enum xpc_retval (*)(partid_t, int, void *, xpc_notify_func, void *)) 5245d9ca49SDean Nelson xpc_notloaded, 5345d9ca49SDean Nelson (void (*)(partid_t, int, void *)) xpc_notloaded, 5445d9ca49SDean Nelson (enum xpc_retval (*)(partid_t, void *)) xpc_notloaded 5545d9ca49SDean Nelson }; 5645d9ca49SDean Nelson 5745d9ca49SDean Nelson 5845d9ca49SDean Nelson /* 5945d9ca49SDean Nelson * XPC calls this when it (the XPC module) has been loaded. 6045d9ca49SDean Nelson */ 6145d9ca49SDean Nelson void 6245d9ca49SDean Nelson xpc_set_interface(void (*connect)(int), 6345d9ca49SDean Nelson void (*disconnect)(int), 6445d9ca49SDean Nelson enum xpc_retval (*allocate)(partid_t, int, u32, void **), 6545d9ca49SDean Nelson enum xpc_retval (*send)(partid_t, int, void *), 6645d9ca49SDean Nelson enum xpc_retval (*send_notify)(partid_t, int, void *, 6745d9ca49SDean Nelson xpc_notify_func, void *), 6845d9ca49SDean Nelson void (*received)(partid_t, int, void *), 6945d9ca49SDean Nelson enum xpc_retval (*partid_to_nasids)(partid_t, void *)) 7045d9ca49SDean Nelson { 7145d9ca49SDean Nelson xpc_interface.connect = connect; 7245d9ca49SDean Nelson xpc_interface.disconnect = disconnect; 7345d9ca49SDean Nelson xpc_interface.allocate = allocate; 7445d9ca49SDean Nelson xpc_interface.send = send; 7545d9ca49SDean Nelson xpc_interface.send_notify = send_notify; 7645d9ca49SDean Nelson xpc_interface.received = received; 7745d9ca49SDean Nelson xpc_interface.partid_to_nasids = partid_to_nasids; 7845d9ca49SDean Nelson } 7945d9ca49SDean Nelson 8045d9ca49SDean Nelson 8145d9ca49SDean Nelson /* 8245d9ca49SDean Nelson * XPC calls this when it (the XPC module) is being unloaded. 8345d9ca49SDean Nelson */ 8445d9ca49SDean Nelson void 8545d9ca49SDean Nelson xpc_clear_interface(void) 8645d9ca49SDean Nelson { 8745d9ca49SDean Nelson xpc_interface.connect = (void (*)(int)) xpc_notloaded; 8845d9ca49SDean Nelson xpc_interface.disconnect = (void (*)(int)) xpc_notloaded; 8945d9ca49SDean Nelson xpc_interface.allocate = (enum xpc_retval (*)(partid_t, int, u32, 9045d9ca49SDean Nelson void **)) xpc_notloaded; 9145d9ca49SDean Nelson xpc_interface.send = (enum xpc_retval (*)(partid_t, int, void *)) 9245d9ca49SDean Nelson xpc_notloaded; 9345d9ca49SDean Nelson xpc_interface.send_notify = (enum xpc_retval (*)(partid_t, int, void *, 9445d9ca49SDean Nelson xpc_notify_func, void *)) xpc_notloaded; 9545d9ca49SDean Nelson xpc_interface.received = (void (*)(partid_t, int, void *)) 9645d9ca49SDean Nelson xpc_notloaded; 9745d9ca49SDean Nelson xpc_interface.partid_to_nasids = (enum xpc_retval (*)(partid_t, void *)) 9845d9ca49SDean Nelson xpc_notloaded; 9945d9ca49SDean Nelson } 10045d9ca49SDean Nelson 10145d9ca49SDean Nelson 10245d9ca49SDean Nelson /* 10345d9ca49SDean Nelson * Register for automatic establishment of a channel connection whenever 10445d9ca49SDean Nelson * a partition comes up. 10545d9ca49SDean Nelson * 10645d9ca49SDean Nelson * Arguments: 10745d9ca49SDean Nelson * 10845d9ca49SDean Nelson * ch_number - channel # to register for connection. 10945d9ca49SDean Nelson * func - function to call for asynchronous notification of channel 11045d9ca49SDean Nelson * state changes (i.e., connection, disconnection, error) and 11145d9ca49SDean Nelson * the arrival of incoming messages. 11245d9ca49SDean Nelson * key - pointer to optional user-defined value that gets passed back 11345d9ca49SDean Nelson * to the user on any callouts made to func. 11445d9ca49SDean Nelson * payload_size - size in bytes of the XPC message's payload area which 11545d9ca49SDean Nelson * contains a user-defined message. The user should make 11645d9ca49SDean Nelson * this large enough to hold their largest message. 11745d9ca49SDean Nelson * nentries - max #of XPC message entries a message queue can contain. 11845d9ca49SDean Nelson * The actual number, which is determined when a connection 11945d9ca49SDean Nelson * is established and may be less then requested, will be 12045d9ca49SDean Nelson * passed to the user via the xpcConnected callout. 12145d9ca49SDean Nelson * assigned_limit - max number of kthreads allowed to be processing 12245d9ca49SDean Nelson * messages (per connection) at any given instant. 12345d9ca49SDean Nelson * idle_limit - max number of kthreads allowed to be idle at any given 12445d9ca49SDean Nelson * instant. 12545d9ca49SDean Nelson */ 12645d9ca49SDean Nelson enum xpc_retval 12745d9ca49SDean Nelson xpc_connect(int ch_number, xpc_channel_func func, void *key, u16 payload_size, 12845d9ca49SDean Nelson u16 nentries, u32 assigned_limit, u32 idle_limit) 12945d9ca49SDean Nelson { 13045d9ca49SDean Nelson struct xpc_registration *registration; 13145d9ca49SDean Nelson 13245d9ca49SDean Nelson 13345d9ca49SDean Nelson DBUG_ON(ch_number < 0 || ch_number >= XPC_NCHANNELS); 13445d9ca49SDean Nelson DBUG_ON(payload_size == 0 || nentries == 0); 13545d9ca49SDean Nelson DBUG_ON(func == NULL); 13645d9ca49SDean Nelson DBUG_ON(assigned_limit == 0 || idle_limit > assigned_limit); 13745d9ca49SDean Nelson 13845d9ca49SDean Nelson registration = &xpc_registrations[ch_number]; 13945d9ca49SDean Nelson 14045d9ca49SDean Nelson if (mutex_lock_interruptible(®istration->mutex) != 0) { 14145d9ca49SDean Nelson return xpcInterrupted; 14245d9ca49SDean Nelson } 14345d9ca49SDean Nelson 14445d9ca49SDean Nelson /* if XPC_CHANNEL_REGISTERED(ch_number) */ 14545d9ca49SDean Nelson if (registration->func != NULL) { 14645d9ca49SDean Nelson mutex_unlock(®istration->mutex); 14745d9ca49SDean Nelson return xpcAlreadyRegistered; 14845d9ca49SDean Nelson } 14945d9ca49SDean Nelson 15045d9ca49SDean Nelson /* register the channel for connection */ 15145d9ca49SDean Nelson registration->msg_size = XPC_MSG_SIZE(payload_size); 15245d9ca49SDean Nelson registration->nentries = nentries; 15345d9ca49SDean Nelson registration->assigned_limit = assigned_limit; 15445d9ca49SDean Nelson registration->idle_limit = idle_limit; 15545d9ca49SDean Nelson registration->key = key; 15645d9ca49SDean Nelson registration->func = func; 15745d9ca49SDean Nelson 15845d9ca49SDean Nelson mutex_unlock(®istration->mutex); 15945d9ca49SDean Nelson 16045d9ca49SDean Nelson xpc_interface.connect(ch_number); 16145d9ca49SDean Nelson 16245d9ca49SDean Nelson return xpcSuccess; 16345d9ca49SDean Nelson } 16445d9ca49SDean Nelson 16545d9ca49SDean Nelson 16645d9ca49SDean Nelson /* 16745d9ca49SDean Nelson * Remove the registration for automatic connection of the specified channel 16845d9ca49SDean Nelson * when a partition comes up. 16945d9ca49SDean Nelson * 17045d9ca49SDean Nelson * Before returning this xpc_disconnect() will wait for all connections on the 17145d9ca49SDean Nelson * specified channel have been closed/torndown. So the caller can be assured 17245d9ca49SDean Nelson * that they will not be receiving any more callouts from XPC to their 17345d9ca49SDean Nelson * function registered via xpc_connect(). 17445d9ca49SDean Nelson * 17545d9ca49SDean Nelson * Arguments: 17645d9ca49SDean Nelson * 17745d9ca49SDean Nelson * ch_number - channel # to unregister. 17845d9ca49SDean Nelson */ 17945d9ca49SDean Nelson void 18045d9ca49SDean Nelson xpc_disconnect(int ch_number) 18145d9ca49SDean Nelson { 18245d9ca49SDean Nelson struct xpc_registration *registration; 18345d9ca49SDean Nelson 18445d9ca49SDean Nelson 18545d9ca49SDean Nelson DBUG_ON(ch_number < 0 || ch_number >= XPC_NCHANNELS); 18645d9ca49SDean Nelson 18745d9ca49SDean Nelson registration = &xpc_registrations[ch_number]; 18845d9ca49SDean Nelson 18945d9ca49SDean Nelson /* 19045d9ca49SDean Nelson * We've decided not to make this a down_interruptible(), since we 19145d9ca49SDean Nelson * figured XPC's users will just turn around and call xpc_disconnect() 19245d9ca49SDean Nelson * again anyways, so we might as well wait, if need be. 19345d9ca49SDean Nelson */ 19445d9ca49SDean Nelson mutex_lock(®istration->mutex); 19545d9ca49SDean Nelson 19645d9ca49SDean Nelson /* if !XPC_CHANNEL_REGISTERED(ch_number) */ 19745d9ca49SDean Nelson if (registration->func == NULL) { 19845d9ca49SDean Nelson mutex_unlock(®istration->mutex); 19945d9ca49SDean Nelson return; 20045d9ca49SDean Nelson } 20145d9ca49SDean Nelson 20245d9ca49SDean Nelson /* remove the connection registration for the specified channel */ 20345d9ca49SDean Nelson registration->func = NULL; 20445d9ca49SDean Nelson registration->key = NULL; 20545d9ca49SDean Nelson registration->nentries = 0; 20645d9ca49SDean Nelson registration->msg_size = 0; 20745d9ca49SDean Nelson registration->assigned_limit = 0; 20845d9ca49SDean Nelson registration->idle_limit = 0; 20945d9ca49SDean Nelson 21045d9ca49SDean Nelson xpc_interface.disconnect(ch_number); 21145d9ca49SDean Nelson 21245d9ca49SDean Nelson mutex_unlock(®istration->mutex); 21345d9ca49SDean Nelson 21445d9ca49SDean Nelson return; 21545d9ca49SDean Nelson } 21645d9ca49SDean Nelson 21745d9ca49SDean Nelson 21845d9ca49SDean Nelson int __init 21945d9ca49SDean Nelson xp_init(void) 22045d9ca49SDean Nelson { 22145d9ca49SDean Nelson int ret, ch_number; 22245d9ca49SDean Nelson u64 func_addr = *(u64 *) xp_nofault_PIOR; 22345d9ca49SDean Nelson u64 err_func_addr = *(u64 *) xp_error_PIOR; 22445d9ca49SDean Nelson 22545d9ca49SDean Nelson 22645d9ca49SDean Nelson if (!ia64_platform_is("sn2")) { 22745d9ca49SDean Nelson return -ENODEV; 22845d9ca49SDean Nelson } 22945d9ca49SDean Nelson 23045d9ca49SDean Nelson /* 23145d9ca49SDean Nelson * Register a nofault code region which performs a cross-partition 23245d9ca49SDean Nelson * PIO read. If the PIO read times out, the MCA handler will consume 23345d9ca49SDean Nelson * the error and return to a kernel-provided instruction to indicate 23445d9ca49SDean Nelson * an error. This PIO read exists because it is guaranteed to timeout 23545d9ca49SDean Nelson * if the destination is down (AMO operations do not timeout on at 23645d9ca49SDean Nelson * least some CPUs on Shubs <= v1.2, which unfortunately we have to 23745d9ca49SDean Nelson * work around). 23845d9ca49SDean Nelson */ 23945d9ca49SDean Nelson if ((ret = sn_register_nofault_code(func_addr, err_func_addr, 24045d9ca49SDean Nelson err_func_addr, 1, 1)) != 0) { 24145d9ca49SDean Nelson printk(KERN_ERR "XP: can't register nofault code, error=%d\n", 24245d9ca49SDean Nelson ret); 24345d9ca49SDean Nelson } 24445d9ca49SDean Nelson /* 24545d9ca49SDean Nelson * Setup the nofault PIO read target. (There is no special reason why 24645d9ca49SDean Nelson * SH_IPI_ACCESS was selected.) 24745d9ca49SDean Nelson */ 24845d9ca49SDean Nelson if (is_shub2()) { 24945d9ca49SDean Nelson xp_nofault_PIOR_target = SH2_IPI_ACCESS0; 25045d9ca49SDean Nelson } else { 25145d9ca49SDean Nelson xp_nofault_PIOR_target = SH1_IPI_ACCESS; 25245d9ca49SDean Nelson } 25345d9ca49SDean Nelson 25445d9ca49SDean Nelson /* initialize the connection registration mutex */ 25545d9ca49SDean Nelson for (ch_number = 0; ch_number < XPC_NCHANNELS; ch_number++) { 25645d9ca49SDean Nelson mutex_init(&xpc_registrations[ch_number].mutex); 25745d9ca49SDean Nelson } 25845d9ca49SDean Nelson 25945d9ca49SDean Nelson return 0; 26045d9ca49SDean Nelson } 26145d9ca49SDean Nelson module_init(xp_init); 26245d9ca49SDean Nelson 26345d9ca49SDean Nelson 26445d9ca49SDean Nelson void __exit 26545d9ca49SDean Nelson xp_exit(void) 26645d9ca49SDean Nelson { 26745d9ca49SDean Nelson u64 func_addr = *(u64 *) xp_nofault_PIOR; 26845d9ca49SDean Nelson u64 err_func_addr = *(u64 *) xp_error_PIOR; 26945d9ca49SDean Nelson 27045d9ca49SDean Nelson 27145d9ca49SDean Nelson /* unregister the PIO read nofault code region */ 27245d9ca49SDean Nelson (void) sn_register_nofault_code(func_addr, err_func_addr, 27345d9ca49SDean Nelson err_func_addr, 1, 0); 27445d9ca49SDean Nelson } 27545d9ca49SDean Nelson module_exit(xp_exit); 27645d9ca49SDean Nelson 27745d9ca49SDean Nelson 27845d9ca49SDean Nelson MODULE_AUTHOR("Silicon Graphics, Inc."); 27945d9ca49SDean Nelson MODULE_DESCRIPTION("Cross Partition (XP) base"); 28045d9ca49SDean Nelson MODULE_LICENSE("GPL"); 28145d9ca49SDean Nelson 28245d9ca49SDean Nelson EXPORT_SYMBOL(xp_nofault_PIOR); 28345d9ca49SDean Nelson EXPORT_SYMBOL(xp_nofault_PIOR_target); 28445d9ca49SDean Nelson EXPORT_SYMBOL(xpc_registrations); 28545d9ca49SDean Nelson EXPORT_SYMBOL(xpc_interface); 28645d9ca49SDean Nelson EXPORT_SYMBOL(xpc_clear_interface); 28745d9ca49SDean Nelson EXPORT_SYMBOL(xpc_set_interface); 28845d9ca49SDean Nelson EXPORT_SYMBOL(xpc_connect); 28945d9ca49SDean Nelson EXPORT_SYMBOL(xpc_disconnect); 29045d9ca49SDean Nelson 291