Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
tiocx.c
Go to the documentation of this file.
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/uaccess.h>
18 #include <asm/sn/sn_sal.h>
19 #include <asm/sn/addrs.h>
20 #include <asm/sn/io.h>
21 #include <asm/sn/types.h>
22 #include <asm/sn/shubio.h>
23 #include <asm/sn/tiocx.h>
24 #include <asm/sn/l1.h>
25 #include <asm/sn/module.h>
26 #include "tio.h"
27 #include "xtalk/xwidgetdev.h"
28 #include "xtalk/hubdev.h"
29 
30 #define CX_DEV_NONE 0
31 #define DEVICE_NAME "tiocx"
32 #define WIDGET_ID 0
33 #define TIOCX_DEBUG 0
34 
35 #if TIOCX_DEBUG
36 #define DBG(fmt...) printk(KERN_ALERT fmt)
37 #else
38 #define DBG(fmt...)
39 #endif
40 
42 
50 static int tiocx_match(struct device *dev, struct device_driver *drv)
51 {
52  struct cx_dev *cx_dev = to_cx_dev(dev);
53  struct cx_drv *cx_drv = to_cx_driver(drv);
54  const struct cx_device_id *ids = cx_drv->id_table;
55 
56  if (!ids)
57  return 0;
58 
59  while (ids->part_num) {
60  if (ids->part_num == cx_dev->cx_id.part_num)
61  return 1;
62  ids++;
63  }
64  return 0;
65 
66 }
67 
68 static int tiocx_uevent(struct device *dev, struct kobj_uevent_env *env)
69 {
70  return -ENODEV;
71 }
72 
73 static void tiocx_bus_release(struct device *dev)
74 {
75  kfree(to_cx_dev(dev));
76 }
77 
84 static const struct cx_device_id *cx_device_match(const struct cx_device_id
85  *ids,
86  struct cx_dev *cx_device)
87 {
88  /*
89  * NOTES: We may want to check for CX_ANY_ID too.
90  * Do we want to match against nasid too?
91  * CX_DEV_NONE == 0, if the driver tries to register for
92  * part/mfg == 0 we should return no-match (NULL) here.
93  */
94  while (ids->part_num && ids->mfg_num) {
95  if (ids->part_num == cx_device->cx_id.part_num &&
96  ids->mfg_num == cx_device->cx_id.mfg_num)
97  return ids;
98  ids++;
99  }
100 
101  return NULL;
102 }
103 
110 static int cx_device_probe(struct device *dev)
111 {
112  const struct cx_device_id *id;
113  struct cx_drv *cx_drv = to_cx_driver(dev->driver);
114  struct cx_dev *cx_dev = to_cx_dev(dev);
115  int error = 0;
116 
117  if (!cx_dev->driver && cx_drv->probe) {
118  id = cx_device_match(cx_drv->id_table, cx_dev);
119  if (id) {
120  if ((error = cx_drv->probe(cx_dev, id)) < 0)
121  return error;
122  else
123  cx_dev->driver = cx_drv;
124  }
125  }
126 
127  return error;
128 }
129 
134 static int cx_driver_remove(struct device *dev)
135 {
136  struct cx_dev *cx_dev = to_cx_dev(dev);
137  struct cx_drv *cx_drv = cx_dev->driver;
138  if (cx_drv->remove)
139  cx_drv->remove(cx_dev);
140  cx_dev->driver = NULL;
141  return 0;
142 }
143 
145  .name = "tiocx",
146  .match = tiocx_match,
147  .uevent = tiocx_uevent,
148  .probe = cx_device_probe,
149  .remove = cx_driver_remove,
150 };
151 
161 int cx_driver_register(struct cx_drv *cx_driver)
162 {
163  cx_driver->driver.name = cx_driver->name;
164  cx_driver->driver.bus = &tiocx_bus_type;
165 
166  return driver_register(&cx_driver->driver);
167 }
168 
173 int cx_driver_unregister(struct cx_drv *cx_driver)
174 {
175  driver_unregister(&cx_driver->driver);
176  return 0;
177 }
178 
188 int
189 cx_device_register(nasid_t nasid, int part_num, int mfg_num,
190  struct hubdev_info *hubdev, int bt)
191 {
192  struct cx_dev *cx_dev;
193  int r;
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  r = device_register(&cx_dev->dev);
211  if (r) {
212  kfree(cx_dev);
213  return r;
214  }
215  get_device(&cx_dev->dev);
216 
218 
219  return 0;
220 }
221 
226 int cx_device_unregister(struct cx_dev *cx_dev)
227 {
228  put_device(&cx_dev->dev);
229  device_unregister(&cx_dev->dev);
230  return 0;
231 }
232 
242 static int cx_device_reload(struct cx_dev *cx_dev)
243 {
244  cx_device_unregister(cx_dev);
245  return cx_device_register(cx_dev->cx_id.nasid, cx_dev->cx_id.part_num,
246  cx_dev->cx_id.mfg_num, cx_dev->hubdev,
247  cx_dev->bt);
248 }
249 
250 static inline u64 tiocx_intr_alloc(nasid_t nasid, int widget,
252  int req_irq, nasid_t req_nasid,
253  int req_slice)
254 {
255  struct ia64_sal_retval rv;
256  rv.status = 0;
257  rv.v0 = 0;
258 
260  SAL_INTR_ALLOC, nasid,
261  widget, sn_irq_info, req_irq,
262  req_nasid, req_slice);
263  return rv.status;
264 }
265 
266 static inline void tiocx_intr_free(nasid_t nasid, int widget,
267  struct sn_irq_info *sn_irq_info)
268 {
269  struct ia64_sal_retval rv;
270  rv.status = 0;
271  rv.v0 = 0;
272 
274  SAL_INTR_FREE, nasid,
275  widget, sn_irq_info->irq_irq,
276  sn_irq_info->irq_cookie, 0, 0);
277 }
278 
279 struct sn_irq_info *tiocx_irq_alloc(nasid_t nasid, int widget, int irq,
280  nasid_t req_nasid, int slice)
281 {
282  struct sn_irq_info *sn_irq_info;
283  int status;
284  int sn_irq_size = sizeof(struct sn_irq_info);
285 
286  if ((nasid & 1) == 0)
287  return NULL;
288 
289  sn_irq_info = kzalloc(sn_irq_size, GFP_KERNEL);
290  if (sn_irq_info == NULL)
291  return NULL;
292 
293  status = tiocx_intr_alloc(nasid, widget, __pa(sn_irq_info), irq,
294  req_nasid, slice);
295  if (status) {
296  kfree(sn_irq_info);
297  return NULL;
298  } else {
299  return sn_irq_info;
300  }
301 }
302 
304 {
305  u64 bridge = (u64) sn_irq_info->irq_bridge;
306  nasid_t nasid = NASID_GET(bridge);
307  int widget;
308 
309  if (nasid & 1) {
310  widget = TIO_SWIN_WIDGETNUM(bridge);
311  tiocx_intr_free(nasid, widget, sn_irq_info);
312  kfree(sn_irq_info);
313  }
314 }
315 
317 {
318  return PHYS_TO_TIODMA(addr);
319 }
320 
322 {
323  return TIO_SWIN_BASE(nasid, TIOCX_CORELET);
324 }
325 
332 EXPORT_SYMBOL(tiocx_bus_type);
335 
336 static void tio_conveyor_set(nasid_t nasid, int enable_flag)
337 {
338  u64 ice_frz;
339  u64 disable_cb = (1ull << 61);
340 
341  if (!(nasid & 1))
342  return;
343 
344  ice_frz = REMOTE_HUB_L(nasid, TIO_ICE_FRZ_CFG);
345  if (enable_flag) {
346  if (!(ice_frz & disable_cb)) /* already enabled */
347  return;
348  ice_frz &= ~disable_cb;
349  } else {
350  if (ice_frz & disable_cb) /* already disabled */
351  return;
352  ice_frz |= disable_cb;
353  }
354  DBG(KERN_ALERT "TIO_ICE_FRZ_CFG= 0x%lx\n", ice_frz);
355  REMOTE_HUB_S(nasid, TIO_ICE_FRZ_CFG, ice_frz);
356 }
357 
358 #define tio_conveyor_enable(nasid) tio_conveyor_set(nasid, 1)
359 #define tio_conveyor_disable(nasid) tio_conveyor_set(nasid, 0)
360 
361 static void tio_corelet_reset(nasid_t nasid, int corelet)
362 {
363  if (!(nasid & 1))
364  return;
365 
366  REMOTE_HUB_S(nasid, TIO_ICE_PMI_TX_CFG, 1 << corelet);
367  udelay(2000);
368  REMOTE_HUB_S(nasid, TIO_ICE_PMI_TX_CFG, 0);
369  udelay(2000);
370 }
371 
372 static int is_fpga_tio(int nasid, int *bt)
373 {
374  u16 uninitialized_var(ioboard_type); /* GCC be quiet */
375  long rc;
376 
377  rc = ia64_sn_sysctl_ioboard_get(nasid, &ioboard_type);
378  if (rc) {
379  printk(KERN_WARNING "ia64_sn_sysctl_ioboard_get failed: %ld\n",
380  rc);
381  return 0;
382  }
383 
384  switch (ioboard_type) {
385  case L1_BRICKTYPE_SA:
386  case L1_BRICKTYPE_ATHENA:
388  *bt = ioboard_type;
389  return 1;
390  }
391 
392  return 0;
393 }
394 
395 static int bitstream_loaded(nasid_t nasid)
396 {
397  u64 cx_credits;
398 
399  cx_credits = REMOTE_HUB_L(nasid, TIO_ICE_PMI_TX_DYN_CREDIT_STAT_CB3);
400  cx_credits &= TIO_ICE_PMI_TX_DYN_CREDIT_STAT_CB3_CREDIT_CNT_MASK;
401  DBG("cx_credits= 0x%lx\n", cx_credits);
402 
403  return (cx_credits == 0xf) ? 1 : 0;
404 }
405 
406 static int tiocx_reload(struct cx_dev *cx_dev)
407 {
408  int part_num = CX_DEV_NONE;
409  int mfg_num = CX_DEV_NONE;
410  nasid_t nasid = cx_dev->cx_id.nasid;
411 
412  if (bitstream_loaded(nasid)) {
413  u64 cx_id;
414  int rv;
415 
416  rv = ia64_sn_sysctl_tio_clock_reset(nasid);
417  if (rv) {
418  printk(KERN_ALERT "CX port JTAG reset failed.\n");
419  } else {
420  cx_id = *(volatile u64 *)
421  (TIO_SWIN_BASE(nasid, TIOCX_CORELET) +
422  WIDGET_ID);
423  part_num = XWIDGET_PART_NUM(cx_id);
424  mfg_num = XWIDGET_MFG_NUM(cx_id);
425  DBG("part= 0x%x, mfg= 0x%x\n", part_num, mfg_num);
426  /* just ignore it if it's a CE */
427  if (part_num == TIO_CE_ASIC_PARTNUM)
428  return 0;
429  }
430  }
431 
432  cx_dev->cx_id.part_num = part_num;
433  cx_dev->cx_id.mfg_num = mfg_num;
434 
435  /*
436  * Delete old device and register the new one. It's ok if
437  * part_num/mfg_num == CX_DEV_NONE. We want to register
438  * devices in the table even if a bitstream isn't loaded.
439  * That allows use to see that a bitstream isn't loaded via
440  * TIOCX_IOCTL_DEV_LIST.
441  */
442  return cx_device_reload(cx_dev);
443 }
444 
445 static ssize_t show_cxdev_control(struct device *dev, struct device_attribute *attr, char *buf)
446 {
447  struct cx_dev *cx_dev = to_cx_dev(dev);
448 
449  return sprintf(buf, "0x%x 0x%x 0x%x 0x%x\n",
450  cx_dev->cx_id.nasid,
451  cx_dev->cx_id.part_num, cx_dev->cx_id.mfg_num,
452  cx_dev->bt);
453 }
454 
455 static ssize_t store_cxdev_control(struct device *dev, struct device_attribute *attr, const char *buf,
456  size_t count)
457 {
458  int n;
459  struct cx_dev *cx_dev = to_cx_dev(dev);
460 
461  if (!capable(CAP_SYS_ADMIN))
462  return -EPERM;
463 
464  if (count <= 0)
465  return 0;
466 
467  n = simple_strtoul(buf, NULL, 0);
468 
469  switch (n) {
470  case 1:
471  tio_corelet_reset(cx_dev->cx_id.nasid, TIOCX_CORELET);
472  tiocx_reload(cx_dev);
473  break;
474  case 2:
475  tiocx_reload(cx_dev);
476  break;
477  case 3:
478  tio_corelet_reset(cx_dev->cx_id.nasid, TIOCX_CORELET);
479  break;
480  default:
481  break;
482  }
483 
484  return count;
485 }
486 
487 DEVICE_ATTR(cxdev_control, 0644, show_cxdev_control, store_cxdev_control);
488 
489 static int __init tiocx_init(void)
490 {
491  cnodeid_t cnodeid;
492  int found_tiocx_device = 0;
493 
494  if (!ia64_platform_is("sn2"))
495  return 0;
496 
497  bus_register(&tiocx_bus_type);
498 
499  for (cnodeid = 0; cnodeid < num_cnodes; cnodeid++) {
500  nasid_t nasid;
501  int bt;
502 
503  nasid = cnodeid_to_nasid(cnodeid);
504 
505  if ((nasid & 0x1) && is_fpga_tio(nasid, &bt)) {
506  struct hubdev_info *hubdev;
507  struct xwidget_info *widgetp;
508 
509  DBG("Found TIO at nasid 0x%x\n", nasid);
510 
511  hubdev =
512  (struct hubdev_info *)(NODEPDA(cnodeid)->pdinfo);
513 
514  widgetp = &hubdev->hdi_xwidget_info[TIOCX_CORELET];
515 
516  /* The CE hangs off of the CX port but is not an FPGA */
517  if (widgetp->xwi_hwid.part_num == TIO_CE_ASIC_PARTNUM)
518  continue;
519 
520  tio_corelet_reset(nasid, TIOCX_CORELET);
521  tio_conveyor_enable(nasid);
522 
524  (nasid, widgetp->xwi_hwid.part_num,
525  widgetp->xwi_hwid.mfg_num, hubdev, bt) < 0)
526  return -ENXIO;
527  else
528  found_tiocx_device++;
529  }
530  }
531 
532  /* It's ok if we find zero devices. */
533  DBG("found_tiocx_device= %d\n", found_tiocx_device);
534 
535  return 0;
536 }
537 
538 static int cx_remove_device(struct device * dev, void * data)
539 {
540  struct cx_dev *cx_dev = to_cx_dev(dev);
542  cx_device_unregister(cx_dev);
543  return 0;
544 }
545 
546 static void __exit tiocx_exit(void)
547 {
548  DBG("tiocx_exit\n");
549 
550  /*
551  * Unregister devices.
552  */
553  bus_for_each_dev(&tiocx_bus_type, NULL, NULL, cx_remove_device);
554  bus_unregister(&tiocx_bus_type);
555 }
556 
557 fs_initcall(tiocx_init);
558 module_exit(tiocx_exit);
559 
560 /************************************************************************
561  * Module licensing and description
562  ************************************************************************/
563 MODULE_LICENSE("GPL");
564 MODULE_AUTHOR("Bruce Losure <[email protected]>");
565 MODULE_DESCRIPTION("TIOCX module");