linux/arch/ia64/sn/kernel/tiocx.c
<<
>>
Prefs
   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
  42struct 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 */
  51static 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
  69static int tiocx_uevent(struct device *dev, struct kobj_uevent_env *env)
  70{
  71        return -ENODEV;
  72}
  73
  74static 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 */
  85static 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 */
 111static 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 */
 135static 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
 145struct 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 */
 162int 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 */
 174int 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 */
 189int
 190cx_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 */
 222int 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 */
 238static 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
 246static 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
 262static 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
 275struct 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
 299void 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
 312u64 tiocx_dma_addr(u64 addr)
 313{
 314        return PHYS_TO_TIODMA(addr);
 315}
 316
 317u64 tiocx_swin_base(int nasid)
 318{
 319        return TIO_SWIN_BASE(nasid, TIOCX_CORELET);
 320}
 321
 322EXPORT_SYMBOL(cx_driver_register);
 323EXPORT_SYMBOL(cx_driver_unregister);
 324EXPORT_SYMBOL(cx_device_register);
 325EXPORT_SYMBOL(cx_device_unregister);
 326EXPORT_SYMBOL(tiocx_irq_alloc);
 327EXPORT_SYMBOL(tiocx_irq_free);
 328EXPORT_SYMBOL(tiocx_bus_type);
 329EXPORT_SYMBOL(tiocx_dma_addr);
 330EXPORT_SYMBOL(tiocx_swin_base);
 331
 332static 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
 357static 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
 368static 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
 391static 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
 402static 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
 441static 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
 451static 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
 483DEVICE_ATTR(cxdev_control, 0644, show_cxdev_control, store_cxdev_control);
 484
 485static 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
 534static 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
 542static 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
 553fs_initcall(tiocx_init);
 554module_exit(tiocx_exit);
 555
 556/************************************************************************
 557 * Module licensing and description
 558 ************************************************************************/
 559MODULE_LICENSE("GPL");
 560MODULE_AUTHOR("Bruce Losure <blosure@sgi.com>");
 561MODULE_DESCRIPTION("TIOCX module");
 562MODULE_SUPPORTED_DEVICE(DEVICE_NAME);
 563