1 /*
2  * This file is subject to the terms and conditions of the GNU General Public
3  * License.  See the file "COPYING" in the main directory of this archive
4  * for more details.
5  *
6  * Copyright (c) 2005 Silicon Graphics, Inc.  All rights reserved.
7  */
8 
9 #include <linux/module.h>
10 #include <linux/kernel.h>
11 #include <linux/slab.h>
12 #include <linux/spinlock.h>
13 #include <linux/proc_fs.h>
14 #include <linux/capability.h>
15 #include <linux/device.h>
16 #include <linux/delay.h>
17 #include <asm/system.h>
18 #include <asm/uaccess.h>
19 #include <asm/sn/sn_sal.h>
20 #include <asm/sn/addrs.h>
21 #include <asm/sn/io.h>
22 #include <asm/sn/types.h>
23 #include <asm/sn/shubio.h>
24 #include <asm/sn/tiocx.h>
25 #include <asm/sn/l1.h>
26 #include <asm/sn/module.h>
27 #include "tio.h"
28 #include "xtalk/xwidgetdev.h"
29 #include "xtalk/hubdev.h"
30 
31 #define CX_DEV_NONE 0
32 #define DEVICE_NAME "tiocx"
33 #define WIDGET_ID 0
34 #define TIOCX_DEBUG 0
35 
36 #if TIOCX_DEBUG
37 #define DBG(fmt...)    printk(KERN_ALERT fmt)
38 #else
39 #define DBG(fmt...)
40 #endif
41 
42 struct device_attribute dev_attr_cxdev_control;
43 
44 /**
45  * tiocx_match - Try to match driver id list with device.
46  * @dev: device pointer
47  * @drv: driver pointer
48  *
49  * Returns 1 if match, 0 otherwise.
50  */
tiocx_match(struct device * dev,struct device_driver * drv)51 static int tiocx_match(struct device *dev, struct device_driver *drv)
52 {
53 	struct cx_dev *cx_dev = to_cx_dev(dev);
54 	struct cx_drv *cx_drv = to_cx_driver(drv);
55 	const struct cx_device_id *ids = cx_drv->id_table;
56 
57 	if (!ids)
58 		return 0;
59 
60 	while (ids->part_num) {
61 		if (ids->part_num == cx_dev->cx_id.part_num)
62 			return 1;
63 		ids++;
64 	}
65 	return 0;
66 
67 }
68 
tiocx_uevent(struct device * dev,struct kobj_uevent_env * env)69 static int tiocx_uevent(struct device *dev, struct kobj_uevent_env *env)
70 {
71 	return -ENODEV;
72 }
73 
tiocx_bus_release(struct device * dev)74 static void tiocx_bus_release(struct device *dev)
75 {
76 	kfree(to_cx_dev(dev));
77 }
78 
79 /**
80  * cx_device_match - Find cx_device in the id table.
81  * @ids: id table from driver
82  * @cx_device: part/mfg id for the device
83  *
84  */
cx_device_match(const struct cx_device_id * ids,struct cx_dev * cx_device)85 static const struct cx_device_id *cx_device_match(const struct cx_device_id
86 						  *ids,
87 						  struct cx_dev *cx_device)
88 {
89 	/*
90 	 * NOTES: We may want to check for CX_ANY_ID too.
91 	 *        Do we want to match against nasid too?
92 	 *        CX_DEV_NONE == 0, if the driver tries to register for
93 	 *        part/mfg == 0 we should return no-match (NULL) here.
94 	 */
95 	while (ids->part_num && ids->mfg_num) {
96 		if (ids->part_num == cx_device->cx_id.part_num &&
97 		    ids->mfg_num == cx_device->cx_id.mfg_num)
98 			return ids;
99 		ids++;
100 	}
101 
102 	return NULL;
103 }
104 
105 /**
106  * cx_device_probe - Look for matching device.
107  *			Call driver probe routine if found.
108  * @cx_driver: driver table (cx_drv struct) from driver
109  * @cx_device: part/mfg id for the device
110  */
cx_device_probe(struct device * dev)111 static int cx_device_probe(struct device *dev)
112 {
113 	const struct cx_device_id *id;
114 	struct cx_drv *cx_drv = to_cx_driver(dev->driver);
115 	struct cx_dev *cx_dev = to_cx_dev(dev);
116 	int error = 0;
117 
118 	if (!cx_dev->driver && cx_drv->probe) {
119 		id = cx_device_match(cx_drv->id_table, cx_dev);
120 		if (id) {
121 			if ((error = cx_drv->probe(cx_dev, id)) < 0)
122 				return error;
123 			else
124 				cx_dev->driver = cx_drv;
125 		}
126 	}
127 
128 	return error;
129 }
130 
131 /**
132  * cx_driver_remove - Remove driver from device struct.
133  * @dev: device
134  */
cx_driver_remove(struct device * dev)135 static int cx_driver_remove(struct device *dev)
136 {
137 	struct cx_dev *cx_dev = to_cx_dev(dev);
138 	struct cx_drv *cx_drv = cx_dev->driver;
139 	if (cx_drv->remove)
140 		cx_drv->remove(cx_dev);
141 	cx_dev->driver = NULL;
142 	return 0;
143 }
144 
145 struct bus_type tiocx_bus_type = {
146 	.name = "tiocx",
147 	.match = tiocx_match,
148 	.uevent = tiocx_uevent,
149 	.probe = cx_device_probe,
150 	.remove = cx_driver_remove,
151 };
152 
153 /**
154  * cx_driver_register - Register the driver.
155  * @cx_driver: driver table (cx_drv struct) from driver
156  *
157  * Called from the driver init routine to register a driver.
158  * The cx_drv struct contains the driver name, a pointer to
159  * a table of part/mfg numbers and a pointer to the driver's
160  * probe/attach routine.
161  */
cx_driver_register(struct cx_drv * cx_driver)162 int cx_driver_register(struct cx_drv *cx_driver)
163 {
164 	cx_driver->driver.name = cx_driver->name;
165 	cx_driver->driver.bus = &tiocx_bus_type;
166 
167 	return driver_register(&cx_driver->driver);
168 }
169 
170 /**
171  * cx_driver_unregister - Unregister the driver.
172  * @cx_driver: driver table (cx_drv struct) from driver
173  */
cx_driver_unregister(struct cx_drv * cx_driver)174 int cx_driver_unregister(struct cx_drv *cx_driver)
175 {
176 	driver_unregister(&cx_driver->driver);
177 	return 0;
178 }
179 
180 /**
181  * cx_device_register - Register a device.
182  * @nasid: device's nasid
183  * @part_num: device's part number
184  * @mfg_num: device's manufacturer number
185  * @hubdev: hub info associated with this device
186  * @bt: board type of the device
187  *
188  */
189 int
cx_device_register(nasid_t nasid,int part_num,int mfg_num,struct hubdev_info * hubdev,int bt)190 cx_device_register(nasid_t nasid, int part_num, int mfg_num,
191 		   struct hubdev_info *hubdev, int bt)
192 {
193 	struct cx_dev *cx_dev;
194 
195 	cx_dev = kzalloc(sizeof(struct cx_dev), GFP_KERNEL);
196 	DBG("cx_dev= 0x%p\n", cx_dev);
197 	if (cx_dev == NULL)
198 		return -ENOMEM;
199 
200 	cx_dev->cx_id.part_num = part_num;
201 	cx_dev->cx_id.mfg_num = mfg_num;
202 	cx_dev->cx_id.nasid = nasid;
203 	cx_dev->hubdev = hubdev;
204 	cx_dev->bt = bt;
205 
206 	cx_dev->dev.parent = NULL;
207 	cx_dev->dev.bus = &tiocx_bus_type;
208 	cx_dev->dev.release = tiocx_bus_release;
209 	dev_set_name(&cx_dev->dev, "%d", cx_dev->cx_id.nasid);
210 	device_register(&cx_dev->dev);
211 	get_device(&cx_dev->dev);
212 
213 	device_create_file(&cx_dev->dev, &dev_attr_cxdev_control);
214 
215 	return 0;
216 }
217 
218 /**
219  * cx_device_unregister - Unregister a device.
220  * @cx_dev: part/mfg id for the device
221  */
cx_device_unregister(struct cx_dev * cx_dev)222 int cx_device_unregister(struct cx_dev *cx_dev)
223 {
224 	put_device(&cx_dev->dev);
225 	device_unregister(&cx_dev->dev);
226 	return 0;
227 }
228 
229 /**
230  * cx_device_reload - Reload the device.
231  * @nasid: device's nasid
232  * @part_num: device's part number
233  * @mfg_num: device's manufacturer number
234  *
235  * Remove the device associated with 'nasid' from device list and then
236  * call device-register with the given part/mfg numbers.
237  */
cx_device_reload(struct cx_dev * cx_dev)238 static int cx_device_reload(struct cx_dev *cx_dev)
239 {
240 	cx_device_unregister(cx_dev);
241 	return cx_device_register(cx_dev->cx_id.nasid, cx_dev->cx_id.part_num,
242 				  cx_dev->cx_id.mfg_num, cx_dev->hubdev,
243 				  cx_dev->bt);
244 }
245 
tiocx_intr_alloc(nasid_t nasid,int widget,u64 sn_irq_info,int req_irq,nasid_t req_nasid,int req_slice)246 static inline u64 tiocx_intr_alloc(nasid_t nasid, int widget,
247 					u64 sn_irq_info,
248 					int req_irq, nasid_t req_nasid,
249 					int req_slice)
250 {
251 	struct ia64_sal_retval rv;
252 	rv.status = 0;
253 	rv.v0 = 0;
254 
255 	ia64_sal_oemcall_nolock(&rv, SN_SAL_IOIF_INTERRUPT,
256 				SAL_INTR_ALLOC, nasid,
257 				widget, sn_irq_info, req_irq,
258 				req_nasid, req_slice);
259 	return rv.status;
260 }
261 
tiocx_intr_free(nasid_t nasid,int widget,struct sn_irq_info * sn_irq_info)262 static inline void tiocx_intr_free(nasid_t nasid, int widget,
263 				   struct sn_irq_info *sn_irq_info)
264 {
265 	struct ia64_sal_retval rv;
266 	rv.status = 0;
267 	rv.v0 = 0;
268 
269 	ia64_sal_oemcall_nolock(&rv, SN_SAL_IOIF_INTERRUPT,
270 				SAL_INTR_FREE, nasid,
271 				widget, sn_irq_info->irq_irq,
272 				sn_irq_info->irq_cookie, 0, 0);
273 }
274 
tiocx_irq_alloc(nasid_t nasid,int widget,int irq,nasid_t req_nasid,int slice)275 struct sn_irq_info *tiocx_irq_alloc(nasid_t nasid, int widget, int irq,
276 				    nasid_t req_nasid, int slice)
277 {
278 	struct sn_irq_info *sn_irq_info;
279 	int status;
280 	int sn_irq_size = sizeof(struct sn_irq_info);
281 
282 	if ((nasid & 1) == 0)
283 		return NULL;
284 
285 	sn_irq_info = kzalloc(sn_irq_size, GFP_KERNEL);
286 	if (sn_irq_info == NULL)
287 		return NULL;
288 
289 	status = tiocx_intr_alloc(nasid, widget, __pa(sn_irq_info), irq,
290 				  req_nasid, slice);
291 	if (status) {
292 		kfree(sn_irq_info);
293 		return NULL;
294 	} else {
295 		return sn_irq_info;
296 	}
297 }
298 
tiocx_irq_free(struct sn_irq_info * sn_irq_info)299 void tiocx_irq_free(struct sn_irq_info *sn_irq_info)
300 {
301 	u64 bridge = (u64) sn_irq_info->irq_bridge;
302 	nasid_t nasid = NASID_GET(bridge);
303 	int widget;
304 
305 	if (nasid & 1) {
306 		widget = TIO_SWIN_WIDGETNUM(bridge);
307 		tiocx_intr_free(nasid, widget, sn_irq_info);
308 		kfree(sn_irq_info);
309 	}
310 }
311 
tiocx_dma_addr(u64 addr)312 u64 tiocx_dma_addr(u64 addr)
313 {
314 	return PHYS_TO_TIODMA(addr);
315 }
316 
tiocx_swin_base(int nasid)317 u64 tiocx_swin_base(int nasid)
318 {
319 	return TIO_SWIN_BASE(nasid, TIOCX_CORELET);
320 }
321 
322 EXPORT_SYMBOL(cx_driver_register);
323 EXPORT_SYMBOL(cx_driver_unregister);
324 EXPORT_SYMBOL(cx_device_register);
325 EXPORT_SYMBOL(cx_device_unregister);
326 EXPORT_SYMBOL(tiocx_irq_alloc);
327 EXPORT_SYMBOL(tiocx_irq_free);
328 EXPORT_SYMBOL(tiocx_bus_type);
329 EXPORT_SYMBOL(tiocx_dma_addr);
330 EXPORT_SYMBOL(tiocx_swin_base);
331 
tio_conveyor_set(nasid_t nasid,int enable_flag)332 static void tio_conveyor_set(nasid_t nasid, int enable_flag)
333 {
334 	u64 ice_frz;
335 	u64 disable_cb = (1ull << 61);
336 
337 	if (!(nasid & 1))
338 		return;
339 
340 	ice_frz = REMOTE_HUB_L(nasid, TIO_ICE_FRZ_CFG);
341 	if (enable_flag) {
342 		if (!(ice_frz & disable_cb))	/* already enabled */
343 			return;
344 		ice_frz &= ~disable_cb;
345 	} else {
346 		if (ice_frz & disable_cb)	/* already disabled */
347 			return;
348 		ice_frz |= disable_cb;
349 	}
350 	DBG(KERN_ALERT "TIO_ICE_FRZ_CFG= 0x%lx\n", ice_frz);
351 	REMOTE_HUB_S(nasid, TIO_ICE_FRZ_CFG, ice_frz);
352 }
353 
354 #define tio_conveyor_enable(nasid) tio_conveyor_set(nasid, 1)
355 #define tio_conveyor_disable(nasid) tio_conveyor_set(nasid, 0)
356 
tio_corelet_reset(nasid_t nasid,int corelet)357 static void tio_corelet_reset(nasid_t nasid, int corelet)
358 {
359 	if (!(nasid & 1))
360 		return;
361 
362 	REMOTE_HUB_S(nasid, TIO_ICE_PMI_TX_CFG, 1 << corelet);
363 	udelay(2000);
364 	REMOTE_HUB_S(nasid, TIO_ICE_PMI_TX_CFG, 0);
365 	udelay(2000);
366 }
367 
is_fpga_tio(int nasid,int * bt)368 static int is_fpga_tio(int nasid, int *bt)
369 {
370 	u16 uninitialized_var(ioboard_type);	/* GCC be quiet */
371 	long rc;
372 
373 	rc = ia64_sn_sysctl_ioboard_get(nasid, &ioboard_type);
374 	if (rc) {
375 		printk(KERN_WARNING "ia64_sn_sysctl_ioboard_get failed: %ld\n",
376 		       rc);
377 		return 0;
378 	}
379 
380 	switch (ioboard_type) {
381 	case L1_BRICKTYPE_SA:
382 	case L1_BRICKTYPE_ATHENA:
383 	case L1_BOARDTYPE_DAYTONA:
384 		*bt = ioboard_type;
385 		return 1;
386 	}
387 
388 	return 0;
389 }
390 
bitstream_loaded(nasid_t nasid)391 static int bitstream_loaded(nasid_t nasid)
392 {
393 	u64 cx_credits;
394 
395 	cx_credits = REMOTE_HUB_L(nasid, TIO_ICE_PMI_TX_DYN_CREDIT_STAT_CB3);
396 	cx_credits &= TIO_ICE_PMI_TX_DYN_CREDIT_STAT_CB3_CREDIT_CNT_MASK;
397 	DBG("cx_credits= 0x%lx\n", cx_credits);
398 
399 	return (cx_credits == 0xf) ? 1 : 0;
400 }
401 
tiocx_reload(struct cx_dev * cx_dev)402 static int tiocx_reload(struct cx_dev *cx_dev)
403 {
404 	int part_num = CX_DEV_NONE;
405 	int mfg_num = CX_DEV_NONE;
406 	nasid_t nasid = cx_dev->cx_id.nasid;
407 
408 	if (bitstream_loaded(nasid)) {
409 		u64 cx_id;
410 		int rv;
411 
412 		rv = ia64_sn_sysctl_tio_clock_reset(nasid);
413 		if (rv) {
414 			printk(KERN_ALERT "CX port JTAG reset failed.\n");
415 		} else {
416 			cx_id = *(volatile u64 *)
417 				(TIO_SWIN_BASE(nasid, TIOCX_CORELET) +
418 					  WIDGET_ID);
419 			part_num = XWIDGET_PART_NUM(cx_id);
420 			mfg_num = XWIDGET_MFG_NUM(cx_id);
421 			DBG("part= 0x%x, mfg= 0x%x\n", part_num, mfg_num);
422 			/* just ignore it if it's a CE */
423 			if (part_num == TIO_CE_ASIC_PARTNUM)
424 				return 0;
425 		}
426 	}
427 
428 	cx_dev->cx_id.part_num = part_num;
429 	cx_dev->cx_id.mfg_num = mfg_num;
430 
431 	/*
432 	 * Delete old device and register the new one.  It's ok if
433 	 * part_num/mfg_num == CX_DEV_NONE.  We want to register
434 	 * devices in the table even if a bitstream isn't loaded.
435 	 * That allows use to see that a bitstream isn't loaded via
436 	 * TIOCX_IOCTL_DEV_LIST.
437 	 */
438 	return cx_device_reload(cx_dev);
439 }
440 
show_cxdev_control(struct device * dev,struct device_attribute * attr,char * buf)441 static ssize_t show_cxdev_control(struct device *dev, struct device_attribute *attr, char *buf)
442 {
443 	struct cx_dev *cx_dev = to_cx_dev(dev);
444 
445 	return sprintf(buf, "0x%x 0x%x 0x%x 0x%x\n",
446 		       cx_dev->cx_id.nasid,
447 		       cx_dev->cx_id.part_num, cx_dev->cx_id.mfg_num,
448 		       cx_dev->bt);
449 }
450 
store_cxdev_control(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)451 static ssize_t store_cxdev_control(struct device *dev, struct device_attribute *attr, const char *buf,
452 				   size_t count)
453 {
454 	int n;
455 	struct cx_dev *cx_dev = to_cx_dev(dev);
456 
457 	if (!capable(CAP_SYS_ADMIN))
458 		return -EPERM;
459 
460 	if (count <= 0)
461 		return 0;
462 
463 	n = simple_strtoul(buf, NULL, 0);
464 
465 	switch (n) {
466 	case 1:
467 		tio_corelet_reset(cx_dev->cx_id.nasid, TIOCX_CORELET);
468 		tiocx_reload(cx_dev);
469 		break;
470 	case 2:
471 		tiocx_reload(cx_dev);
472 		break;
473 	case 3:
474 		tio_corelet_reset(cx_dev->cx_id.nasid, TIOCX_CORELET);
475 		break;
476 	default:
477 		break;
478 	}
479 
480 	return count;
481 }
482 
483 DEVICE_ATTR(cxdev_control, 0644, show_cxdev_control, store_cxdev_control);
484 
tiocx_init(void)485 static int __init tiocx_init(void)
486 {
487 	cnodeid_t cnodeid;
488 	int found_tiocx_device = 0;
489 
490 	if (!ia64_platform_is("sn2"))
491 		return 0;
492 
493 	bus_register(&tiocx_bus_type);
494 
495 	for (cnodeid = 0; cnodeid < num_cnodes; cnodeid++) {
496 		nasid_t nasid;
497 		int bt;
498 
499 		nasid = cnodeid_to_nasid(cnodeid);
500 
501 		if ((nasid & 0x1) && is_fpga_tio(nasid, &bt)) {
502 			struct hubdev_info *hubdev;
503 			struct xwidget_info *widgetp;
504 
505 			DBG("Found TIO at nasid 0x%x\n", nasid);
506 
507 			hubdev =
508 			    (struct hubdev_info *)(NODEPDA(cnodeid)->pdinfo);
509 
510 			widgetp = &hubdev->hdi_xwidget_info[TIOCX_CORELET];
511 
512 			/* The CE hangs off of the CX port but is not an FPGA */
513 			if (widgetp->xwi_hwid.part_num == TIO_CE_ASIC_PARTNUM)
514 				continue;
515 
516 			tio_corelet_reset(nasid, TIOCX_CORELET);
517 			tio_conveyor_enable(nasid);
518 
519 			if (cx_device_register
520 			    (nasid, widgetp->xwi_hwid.part_num,
521 			     widgetp->xwi_hwid.mfg_num, hubdev, bt) < 0)
522 				return -ENXIO;
523 			else
524 				found_tiocx_device++;
525 		}
526 	}
527 
528 	/* It's ok if we find zero devices. */
529 	DBG("found_tiocx_device= %d\n", found_tiocx_device);
530 
531 	return 0;
532 }
533 
cx_remove_device(struct device * dev,void * data)534 static int cx_remove_device(struct device * dev, void * data)
535 {
536 	struct cx_dev *cx_dev = to_cx_dev(dev);
537 	device_remove_file(dev, &dev_attr_cxdev_control);
538 	cx_device_unregister(cx_dev);
539 	return 0;
540 }
541 
tiocx_exit(void)542 static void __exit tiocx_exit(void)
543 {
544 	DBG("tiocx_exit\n");
545 
546 	/*
547 	 * Unregister devices.
548 	 */
549 	bus_for_each_dev(&tiocx_bus_type, NULL, NULL, cx_remove_device);
550 	bus_unregister(&tiocx_bus_type);
551 }
552 
553 fs_initcall(tiocx_init);
554 module_exit(tiocx_exit);
555 
556 /************************************************************************
557  * Module licensing and description
558  ************************************************************************/
559 MODULE_LICENSE("GPL");
560 MODULE_AUTHOR("Bruce Losure <blosure@sgi.com>");
561 MODULE_DESCRIPTION("TIOCX module");
562 MODULE_SUPPORTED_DEVICE(DEVICE_NAME);
563