| 
									
										
										
										
											2005-04-25 13:12:02 -07:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * This file is subject to the terms and conditions of the GNU General Public | 
					
						
							|  |  |  |  * License.  See the file "COPYING" in the main directory of this archive | 
					
						
							|  |  |  |  * for more details. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright (c) 2005 Silicon Graphics, Inc.  All rights reserved. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <linux/module.h>
 | 
					
						
							|  |  |  | #include <linux/kernel.h>
 | 
					
						
							|  |  |  | #include <linux/version.h>
 | 
					
						
							|  |  |  | #include <linux/slab.h>
 | 
					
						
							|  |  |  | #include <linux/spinlock.h>
 | 
					
						
							|  |  |  | #include <linux/proc_fs.h>
 | 
					
						
							|  |  |  | #include <linux/device.h>
 | 
					
						
							|  |  |  | #include <linux/delay.h>
 | 
					
						
							|  |  |  | #include <asm/uaccess.h>
 | 
					
						
							|  |  |  | #include <asm/sn/sn_sal.h>
 | 
					
						
							|  |  |  | #include <asm/sn/addrs.h>
 | 
					
						
							|  |  |  | #include <asm/sn/io.h>
 | 
					
						
							|  |  |  | #include <asm/sn/types.h>
 | 
					
						
							|  |  |  | #include <asm/sn/shubio.h>
 | 
					
						
							|  |  |  | #include <asm/sn/tiocx.h>
 | 
					
						
							| 
									
										
										
										
											2005-04-25 19:41:00 -07:00
										 |  |  | #include <asm/sn/l1.h>
 | 
					
						
							|  |  |  | #include <asm/sn/module.h>
 | 
					
						
							| 
									
										
										
										
											2005-04-25 13:12:02 -07:00
										 |  |  | #include "tio.h"
 | 
					
						
							|  |  |  | #include "xtalk/xwidgetdev.h"
 | 
					
						
							|  |  |  | #include "xtalk/hubdev.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define CX_DEV_NONE 0
 | 
					
						
							|  |  |  | #define DEVICE_NAME "tiocx"
 | 
					
						
							|  |  |  | #define WIDGET_ID 0
 | 
					
						
							|  |  |  | #define TIOCX_DEBUG 0
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #if TIOCX_DEBUG
 | 
					
						
							|  |  |  | #define DBG(fmt...)    printk(KERN_ALERT fmt)
 | 
					
						
							|  |  |  | #else
 | 
					
						
							|  |  |  | #define DBG(fmt...)
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct device_attribute dev_attr_cxdev_control; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * tiocx_match - Try to match driver id list with device. | 
					
						
							|  |  |  |  * @dev: device pointer | 
					
						
							|  |  |  |  * @drv: driver pointer | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Returns 1 if match, 0 otherwise. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static int tiocx_match(struct device *dev, struct device_driver *drv) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct cx_dev *cx_dev = to_cx_dev(dev); | 
					
						
							|  |  |  | 	struct cx_drv *cx_drv = to_cx_driver(drv); | 
					
						
							|  |  |  | 	const struct cx_device_id *ids = cx_drv->id_table; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!ids) | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	while (ids->part_num) { | 
					
						
							|  |  |  | 		if (ids->part_num == cx_dev->cx_id.part_num) | 
					
						
							|  |  |  | 			return 1; | 
					
						
							|  |  |  | 		ids++; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int tiocx_hotplug(struct device *dev, char **envp, int num_envp, | 
					
						
							|  |  |  | 			 char *buffer, int buffer_size) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return -ENODEV; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void tiocx_bus_release(struct device *dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	kfree(to_cx_dev(dev)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct bus_type tiocx_bus_type = { | 
					
						
							|  |  |  | 	.name = "tiocx", | 
					
						
							|  |  |  | 	.match = tiocx_match, | 
					
						
							|  |  |  | 	.hotplug = tiocx_hotplug, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * cx_device_match - Find cx_device in the id table. | 
					
						
							|  |  |  |  * @ids: id table from driver | 
					
						
							|  |  |  |  * @cx_device: part/mfg id for the device | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static const struct cx_device_id *cx_device_match(const struct cx_device_id | 
					
						
							|  |  |  | 						  *ids, | 
					
						
							|  |  |  | 						  struct cx_dev *cx_device) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * NOTES: We may want to check for CX_ANY_ID too. | 
					
						
							|  |  |  | 	 *        Do we want to match against nasid too? | 
					
						
							|  |  |  | 	 *        CX_DEV_NONE == 0, if the driver tries to register for | 
					
						
							|  |  |  | 	 *        part/mfg == 0 we should return no-match (NULL) here. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	while (ids->part_num && ids->mfg_num) { | 
					
						
							|  |  |  | 		if (ids->part_num == cx_device->cx_id.part_num && | 
					
						
							|  |  |  | 		    ids->mfg_num == cx_device->cx_id.mfg_num) | 
					
						
							|  |  |  | 			return ids; | 
					
						
							|  |  |  | 		ids++; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return NULL; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * cx_device_probe - Look for matching device. | 
					
						
							|  |  |  |  *			Call driver probe routine if found. | 
					
						
							|  |  |  |  * @cx_driver: driver table (cx_drv struct) from driver | 
					
						
							|  |  |  |  * @cx_device: part/mfg id for the device | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static int cx_device_probe(struct device *dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	const struct cx_device_id *id; | 
					
						
							|  |  |  | 	struct cx_drv *cx_drv = to_cx_driver(dev->driver); | 
					
						
							|  |  |  | 	struct cx_dev *cx_dev = to_cx_dev(dev); | 
					
						
							|  |  |  | 	int error = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!cx_dev->driver && cx_drv->probe) { | 
					
						
							|  |  |  | 		id = cx_device_match(cx_drv->id_table, cx_dev); | 
					
						
							|  |  |  | 		if (id) { | 
					
						
							|  |  |  | 			if ((error = cx_drv->probe(cx_dev, id)) < 0) | 
					
						
							|  |  |  | 				return error; | 
					
						
							|  |  |  | 			else | 
					
						
							|  |  |  | 				cx_dev->driver = cx_drv; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return error; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * cx_driver_remove - Remove driver from device struct. | 
					
						
							|  |  |  |  * @dev: device | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static int cx_driver_remove(struct device *dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct cx_dev *cx_dev = to_cx_dev(dev); | 
					
						
							|  |  |  | 	struct cx_drv *cx_drv = cx_dev->driver; | 
					
						
							|  |  |  | 	if (cx_drv->remove) | 
					
						
							|  |  |  | 		cx_drv->remove(cx_dev); | 
					
						
							|  |  |  | 	cx_dev->driver = NULL; | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * cx_driver_register - Register the driver. | 
					
						
							|  |  |  |  * @cx_driver: driver table (cx_drv struct) from driver | 
					
						
							|  |  |  |  *  | 
					
						
							|  |  |  |  * Called from the driver init routine to register a driver. | 
					
						
							|  |  |  |  * The cx_drv struct contains the driver name, a pointer to | 
					
						
							|  |  |  |  * a table of part/mfg numbers and a pointer to the driver's | 
					
						
							|  |  |  |  * probe/attach routine. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | int cx_driver_register(struct cx_drv *cx_driver) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	cx_driver->driver.name = cx_driver->name; | 
					
						
							|  |  |  | 	cx_driver->driver.bus = &tiocx_bus_type; | 
					
						
							|  |  |  | 	cx_driver->driver.probe = cx_device_probe; | 
					
						
							|  |  |  | 	cx_driver->driver.remove = cx_driver_remove; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return driver_register(&cx_driver->driver); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * cx_driver_unregister - Unregister the driver. | 
					
						
							|  |  |  |  * @cx_driver: driver table (cx_drv struct) from driver | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | int cx_driver_unregister(struct cx_drv *cx_driver) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	driver_unregister(&cx_driver->driver); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * cx_device_register - Register a device. | 
					
						
							|  |  |  |  * @nasid: device's nasid | 
					
						
							|  |  |  |  * @part_num: device's part number | 
					
						
							|  |  |  |  * @mfg_num: device's manufacturer number | 
					
						
							|  |  |  |  * @hubdev: hub info associated with this device | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | int | 
					
						
							|  |  |  | cx_device_register(nasid_t nasid, int part_num, int mfg_num, | 
					
						
							|  |  |  | 		   struct hubdev_info *hubdev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct cx_dev *cx_dev; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	cx_dev = kcalloc(1, sizeof(struct cx_dev), GFP_KERNEL); | 
					
						
							|  |  |  | 	DBG("cx_dev= 0x%p\n", cx_dev); | 
					
						
							|  |  |  | 	if (cx_dev == NULL) | 
					
						
							|  |  |  | 		return -ENOMEM; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	cx_dev->cx_id.part_num = part_num; | 
					
						
							|  |  |  | 	cx_dev->cx_id.mfg_num = mfg_num; | 
					
						
							|  |  |  | 	cx_dev->cx_id.nasid = nasid; | 
					
						
							|  |  |  | 	cx_dev->hubdev = hubdev; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	cx_dev->dev.parent = NULL; | 
					
						
							|  |  |  | 	cx_dev->dev.bus = &tiocx_bus_type; | 
					
						
							|  |  |  | 	cx_dev->dev.release = tiocx_bus_release; | 
					
						
							| 
									
										
										
										
											2005-05-24 08:30:00 -07:00
										 |  |  | 	snprintf(cx_dev->dev.bus_id, BUS_ID_SIZE, "%d", | 
					
						
							|  |  |  | 		 cx_dev->cx_id.nasid); | 
					
						
							| 
									
										
										
										
											2005-04-25 13:12:02 -07:00
										 |  |  | 	device_register(&cx_dev->dev); | 
					
						
							|  |  |  | 	get_device(&cx_dev->dev); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	device_create_file(&cx_dev->dev, &dev_attr_cxdev_control); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * cx_device_unregister - Unregister a device. | 
					
						
							|  |  |  |  * @cx_dev: part/mfg id for the device | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | int cx_device_unregister(struct cx_dev *cx_dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	put_device(&cx_dev->dev); | 
					
						
							|  |  |  | 	device_unregister(&cx_dev->dev); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * cx_device_reload - Reload the device. | 
					
						
							|  |  |  |  * @nasid: device's nasid | 
					
						
							|  |  |  |  * @part_num: device's part number | 
					
						
							|  |  |  |  * @mfg_num: device's manufacturer number | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Remove the device associated with 'nasid' from device list and then | 
					
						
							|  |  |  |  * call device-register with the given part/mfg numbers. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static int cx_device_reload(struct cx_dev *cx_dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	cx_device_unregister(cx_dev); | 
					
						
							|  |  |  | 	return cx_device_register(cx_dev->cx_id.nasid, cx_dev->cx_id.part_num, | 
					
						
							|  |  |  | 				  cx_dev->cx_id.mfg_num, cx_dev->hubdev); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static inline uint64_t tiocx_intr_alloc(nasid_t nasid, int widget, | 
					
						
							|  |  |  | 					u64 sn_irq_info, | 
					
						
							|  |  |  | 					int req_irq, nasid_t req_nasid, | 
					
						
							|  |  |  | 					int req_slice) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct ia64_sal_retval rv; | 
					
						
							|  |  |  | 	rv.status = 0; | 
					
						
							|  |  |  | 	rv.v0 = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ia64_sal_oemcall_nolock(&rv, SN_SAL_IOIF_INTERRUPT, | 
					
						
							|  |  |  | 				SAL_INTR_ALLOC, nasid, | 
					
						
							|  |  |  | 				widget, sn_irq_info, req_irq, | 
					
						
							|  |  |  | 				req_nasid, req_slice); | 
					
						
							|  |  |  | 	return rv.status; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static inline void tiocx_intr_free(nasid_t nasid, int widget, | 
					
						
							|  |  |  | 				   struct sn_irq_info *sn_irq_info) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct ia64_sal_retval rv; | 
					
						
							|  |  |  | 	rv.status = 0; | 
					
						
							|  |  |  | 	rv.v0 = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ia64_sal_oemcall_nolock(&rv, SN_SAL_IOIF_INTERRUPT, | 
					
						
							|  |  |  | 				SAL_INTR_FREE, nasid, | 
					
						
							|  |  |  | 				widget, sn_irq_info->irq_irq, | 
					
						
							|  |  |  | 				sn_irq_info->irq_cookie, 0, 0); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct sn_irq_info *tiocx_irq_alloc(nasid_t nasid, int widget, int irq, | 
					
						
							|  |  |  | 				    nasid_t req_nasid, int slice) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct sn_irq_info *sn_irq_info; | 
					
						
							|  |  |  | 	int status; | 
					
						
							|  |  |  | 	int sn_irq_size = sizeof(struct sn_irq_info); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if ((nasid & 1) == 0) | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	sn_irq_info = kmalloc(sn_irq_size, GFP_KERNEL); | 
					
						
							|  |  |  | 	if (sn_irq_info == NULL) | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	memset(sn_irq_info, 0x0, sn_irq_size); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	status = tiocx_intr_alloc(nasid, widget, __pa(sn_irq_info), irq, | 
					
						
							|  |  |  | 				  req_nasid, slice); | 
					
						
							|  |  |  | 	if (status) { | 
					
						
							|  |  |  | 		kfree(sn_irq_info); | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		return sn_irq_info; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void tiocx_irq_free(struct sn_irq_info *sn_irq_info) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	uint64_t bridge = (uint64_t) sn_irq_info->irq_bridge; | 
					
						
							|  |  |  | 	nasid_t nasid = NASID_GET(bridge); | 
					
						
							|  |  |  | 	int widget; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (nasid & 1) { | 
					
						
							|  |  |  | 		widget = TIO_SWIN_WIDGETNUM(bridge); | 
					
						
							|  |  |  | 		tiocx_intr_free(nasid, widget, sn_irq_info); | 
					
						
							|  |  |  | 		kfree(sn_irq_info); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-25 19:41:00 -07:00
										 |  |  | uint64_t tiocx_dma_addr(uint64_t addr) | 
					
						
							| 
									
										
										
										
											2005-04-25 13:12:02 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	return PHYS_TO_TIODMA(addr); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-25 19:41:00 -07:00
										 |  |  | uint64_t tiocx_swin_base(int nasid) | 
					
						
							| 
									
										
										
										
											2005-04-25 13:12:02 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	return TIO_SWIN_BASE(nasid, TIOCX_CORELET); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | EXPORT_SYMBOL(cx_driver_register); | 
					
						
							|  |  |  | EXPORT_SYMBOL(cx_driver_unregister); | 
					
						
							|  |  |  | EXPORT_SYMBOL(cx_device_register); | 
					
						
							|  |  |  | EXPORT_SYMBOL(cx_device_unregister); | 
					
						
							|  |  |  | EXPORT_SYMBOL(tiocx_irq_alloc); | 
					
						
							|  |  |  | EXPORT_SYMBOL(tiocx_irq_free); | 
					
						
							|  |  |  | EXPORT_SYMBOL(tiocx_bus_type); | 
					
						
							|  |  |  | EXPORT_SYMBOL(tiocx_dma_addr); | 
					
						
							|  |  |  | EXPORT_SYMBOL(tiocx_swin_base); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void tio_conveyor_set(nasid_t nasid, int enable_flag) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	uint64_t ice_frz; | 
					
						
							|  |  |  | 	uint64_t disable_cb = (1ull << 61); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!(nasid & 1)) | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ice_frz = REMOTE_HUB_L(nasid, TIO_ICE_FRZ_CFG); | 
					
						
							|  |  |  | 	if (enable_flag) { | 
					
						
							|  |  |  | 		if (!(ice_frz & disable_cb))	/* already enabled */ | 
					
						
							|  |  |  | 			return; | 
					
						
							|  |  |  | 		ice_frz &= ~disable_cb; | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		if (ice_frz & disable_cb)	/* already disabled */ | 
					
						
							|  |  |  | 			return; | 
					
						
							|  |  |  | 		ice_frz |= disable_cb; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	DBG(KERN_ALERT "TIO_ICE_FRZ_CFG= 0x%lx\n", ice_frz); | 
					
						
							|  |  |  | 	REMOTE_HUB_S(nasid, TIO_ICE_FRZ_CFG, ice_frz); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define tio_conveyor_enable(nasid) tio_conveyor_set(nasid, 1)
 | 
					
						
							|  |  |  | #define tio_conveyor_disable(nasid) tio_conveyor_set(nasid, 0)
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void tio_corelet_reset(nasid_t nasid, int corelet) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	if (!(nasid & 1)) | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	REMOTE_HUB_S(nasid, TIO_ICE_PMI_TX_CFG, 1 << corelet); | 
					
						
							|  |  |  | 	udelay(2000); | 
					
						
							|  |  |  | 	REMOTE_HUB_S(nasid, TIO_ICE_PMI_TX_CFG, 0); | 
					
						
							|  |  |  | 	udelay(2000); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-25 19:41:00 -07:00
										 |  |  | static int tiocx_btchar_get(int nasid) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	moduleid_t module_id; | 
					
						
							|  |  |  | 	geoid_t geoid; | 
					
						
							|  |  |  | 	int cnodeid; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	cnodeid = nasid_to_cnodeid(nasid); | 
					
						
							|  |  |  | 	geoid = cnodeid_get_geoid(cnodeid); | 
					
						
							|  |  |  | 	module_id = geo_module(geoid); | 
					
						
							|  |  |  | 	return MODULE_GET_BTCHAR(module_id); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int is_fpga_brick(int nasid) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	switch (tiocx_btchar_get(nasid)) { | 
					
						
							|  |  |  | 	case L1_BRICKTYPE_SA: | 
					
						
							|  |  |  | 	case L1_BRICKTYPE_ATHENA: | 
					
						
							| 
									
										
										
										
											2005-05-24 08:30:00 -07:00
										 |  |  | 	case L1_BRICKTYPE_DAYTONA: | 
					
						
							| 
									
										
										
										
											2005-04-25 19:41:00 -07:00
										 |  |  | 		return 1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int bitstream_loaded(nasid_t nasid) | 
					
						
							| 
									
										
										
										
											2005-04-25 13:12:02 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	uint64_t cx_credits; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	cx_credits = REMOTE_HUB_L(nasid, TIO_ICE_PMI_TX_DYN_CREDIT_STAT_CB3); | 
					
						
							|  |  |  | 	cx_credits &= TIO_ICE_PMI_TX_DYN_CREDIT_STAT_CB3_CREDIT_CNT_MASK; | 
					
						
							|  |  |  | 	DBG("cx_credits= 0x%lx\n", cx_credits); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return (cx_credits == 0xf) ? 1 : 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int tiocx_reload(struct cx_dev *cx_dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int part_num = CX_DEV_NONE; | 
					
						
							|  |  |  | 	int mfg_num = CX_DEV_NONE; | 
					
						
							|  |  |  | 	nasid_t nasid = cx_dev->cx_id.nasid; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-25 19:41:00 -07:00
										 |  |  | 	if (bitstream_loaded(nasid)) { | 
					
						
							| 
									
										
										
										
											2005-04-25 13:12:02 -07:00
										 |  |  | 		uint64_t cx_id; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		cx_id = | 
					
						
							| 
									
										
										
										
											2005-05-24 08:30:00 -07:00
										 |  |  | 		    *(volatile uint64_t *)(TIO_SWIN_BASE(nasid, TIOCX_CORELET) + | 
					
						
							| 
									
										
										
										
											2005-04-25 13:12:02 -07:00
										 |  |  | 					  WIDGET_ID); | 
					
						
							|  |  |  | 		part_num = XWIDGET_PART_NUM(cx_id); | 
					
						
							|  |  |  | 		mfg_num = XWIDGET_MFG_NUM(cx_id); | 
					
						
							|  |  |  | 		DBG("part= 0x%x, mfg= 0x%x\n", part_num, mfg_num); | 
					
						
							|  |  |  | 		/* just ignore it if it's a CE */ | 
					
						
							|  |  |  | 		if (part_num == TIO_CE_ASIC_PARTNUM) | 
					
						
							|  |  |  | 			return 0; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	cx_dev->cx_id.part_num = part_num; | 
					
						
							|  |  |  | 	cx_dev->cx_id.mfg_num = mfg_num; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Delete old device and register the new one.  It's ok if | 
					
						
							|  |  |  | 	 * part_num/mfg_num == CX_DEV_NONE.  We want to register | 
					
						
							|  |  |  | 	 * devices in the table even if a bitstream isn't loaded. | 
					
						
							|  |  |  | 	 * That allows use to see that a bitstream isn't loaded via | 
					
						
							|  |  |  | 	 * TIOCX_IOCTL_DEV_LIST. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	return cx_device_reload(cx_dev); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-05-17 06:40:51 -04:00
										 |  |  | static ssize_t show_cxdev_control(struct device *dev, struct device_attribute *attr, char *buf) | 
					
						
							| 
									
										
										
										
											2005-04-25 13:12:02 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct cx_dev *cx_dev = to_cx_dev(dev); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-25 19:41:00 -07:00
										 |  |  | 	return sprintf(buf, "0x%x 0x%x 0x%x %d\n", | 
					
						
							| 
									
										
										
										
											2005-04-25 13:12:02 -07:00
										 |  |  | 		       cx_dev->cx_id.nasid, | 
					
						
							| 
									
										
										
										
											2005-04-25 19:41:00 -07:00
										 |  |  | 		       cx_dev->cx_id.part_num, cx_dev->cx_id.mfg_num, | 
					
						
							|  |  |  | 		       tiocx_btchar_get(cx_dev->cx_id.nasid)); | 
					
						
							| 
									
										
										
										
											2005-04-25 13:12:02 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-05-17 06:40:51 -04:00
										 |  |  | static ssize_t store_cxdev_control(struct device *dev, struct device_attribute *attr, const char *buf, | 
					
						
							| 
									
										
										
										
											2005-04-25 13:12:02 -07:00
										 |  |  | 				   size_t count) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int n; | 
					
						
							|  |  |  | 	struct cx_dev *cx_dev = to_cx_dev(dev); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!capable(CAP_SYS_ADMIN)) | 
					
						
							|  |  |  | 		return -EPERM; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (count <= 0) | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	n = simple_strtoul(buf, NULL, 0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch (n) { | 
					
						
							|  |  |  | 	case 1: | 
					
						
							| 
									
										
										
										
											2005-05-24 08:30:00 -07:00
										 |  |  | 		tio_corelet_reset(cx_dev->cx_id.nasid, TIOCX_CORELET); | 
					
						
							|  |  |  | 		tiocx_reload(cx_dev); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case 2: | 
					
						
							| 
									
										
										
										
											2005-04-25 13:12:02 -07:00
										 |  |  | 		tiocx_reload(cx_dev); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case 3: | 
					
						
							|  |  |  | 		tio_corelet_reset(cx_dev->cx_id.nasid, TIOCX_CORELET); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return count; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | DEVICE_ATTR(cxdev_control, 0644, show_cxdev_control, store_cxdev_control); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int __init tiocx_init(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	cnodeid_t cnodeid; | 
					
						
							|  |  |  | 	int found_tiocx_device = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	bus_register(&tiocx_bus_type); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (cnodeid = 0; cnodeid < MAX_COMPACT_NODES; cnodeid++) { | 
					
						
							|  |  |  | 		nasid_t nasid; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if ((nasid = cnodeid_to_nasid(cnodeid)) < 0) | 
					
						
							|  |  |  | 			break;	/* No more nasids .. bail out of loop */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-25 19:41:00 -07:00
										 |  |  | 		if ((nasid & 0x1) && is_fpga_brick(nasid)) { | 
					
						
							| 
									
										
										
										
											2005-04-25 13:12:02 -07:00
										 |  |  | 			struct hubdev_info *hubdev; | 
					
						
							|  |  |  | 			struct xwidget_info *widgetp; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			DBG("Found TIO at nasid 0x%x\n", nasid); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			hubdev = | 
					
						
							|  |  |  | 			    (struct hubdev_info *)(NODEPDA(cnodeid)->pdinfo); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			widgetp = &hubdev->hdi_xwidget_info[TIOCX_CORELET]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			/* The CE hangs off of the CX port but is not an FPGA */ | 
					
						
							|  |  |  | 			if (widgetp->xwi_hwid.part_num == TIO_CE_ASIC_PARTNUM) | 
					
						
							|  |  |  | 				continue; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			tio_corelet_reset(nasid, TIOCX_CORELET); | 
					
						
							|  |  |  | 			tio_conveyor_enable(nasid); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if (cx_device_register | 
					
						
							|  |  |  | 			    (nasid, widgetp->xwi_hwid.part_num, | 
					
						
							|  |  |  | 			     widgetp->xwi_hwid.mfg_num, hubdev) < 0) | 
					
						
							|  |  |  | 				return -ENXIO; | 
					
						
							|  |  |  | 			else | 
					
						
							|  |  |  | 				found_tiocx_device++; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* It's ok if we find zero devices. */ | 
					
						
							|  |  |  | 	DBG("found_tiocx_device= %d\n", found_tiocx_device); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-28 17:11:52 -07:00
										 |  |  | static int cx_remove_device(struct device * dev, void * data) | 
					
						
							| 
									
										
										
										
											2005-04-25 13:12:02 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2005-04-28 17:11:52 -07:00
										 |  |  | 	struct cx_dev *cx_dev = to_cx_dev(dev); | 
					
						
							|  |  |  | 	device_remove_file(dev, &dev_attr_cxdev_control); | 
					
						
							|  |  |  | 	cx_device_unregister(cx_dev); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2005-04-25 13:12:02 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-28 17:11:52 -07:00
										 |  |  | static void __exit tiocx_exit(void) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2005-04-25 13:12:02 -07:00
										 |  |  | 	DBG("tiocx_exit\n"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Unregister devices. | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2005-04-28 17:11:52 -07:00
										 |  |  | 	bus_for_each_dev(&tiocx_bus_type, NULL, NULL, cx_remove_device); | 
					
						
							| 
									
										
										
										
											2005-04-25 13:12:02 -07:00
										 |  |  | 	bus_unregister(&tiocx_bus_type); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-05-24 08:30:00 -07:00
										 |  |  | subsys_initcall(tiocx_init); | 
					
						
							| 
									
										
										
										
											2005-04-25 13:12:02 -07:00
										 |  |  | module_exit(tiocx_exit); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /************************************************************************
 | 
					
						
							|  |  |  |  * Module licensing and description | 
					
						
							|  |  |  |  ************************************************************************/ | 
					
						
							|  |  |  | MODULE_LICENSE("GPL"); | 
					
						
							|  |  |  | MODULE_AUTHOR("Bruce Losure <blosure@sgi.com>"); | 
					
						
							|  |  |  | MODULE_DESCRIPTION("TIOCX module"); | 
					
						
							|  |  |  | MODULE_SUPPORTED_DEVICE(DEVICE_NAME); |