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 <linux/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
  41struct device_attribute dev_attr_cxdev_control;
  42
  43/**
  44 * tiocx_match - Try to match driver id list with device.
  45 * @dev: device pointer
  46 * @drv: driver pointer
  47 *
  48 * Returns 1 if match, 0 otherwise.
  49 */
  50static 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
  68static int tiocx_uevent(struct device *dev, struct kobj_uevent_env *env)
  69{
  70        return -ENODEV;
  71}
  72
  73static void tiocx_bus_release(struct device *dev)
  74{
  75        kfree(to_cx_dev(dev));
  76}
  77
  78/**
  79 * cx_device_match - Find cx_device in the id table.
  80 * @ids: id table from driver
  81 * @cx_device: part/mfg id for the device
  82 *
  83 */
  84static 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
 104/**
 105 * cx_device_probe - Look for matching device.
 106 *                      Call driver probe routine if found.
 107 * @cx_driver: driver table (cx_drv struct) from driver
 108 * @cx_device: part/mfg id for the device
 109 */
 110static 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
 130/**
 131 * cx_driver_remove - Remove driver from device struct.
 132 * @dev: device
 133 */
 134static 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
 144struct bus_type tiocx_bus_type = {
 145        .name = "tiocx",
 146        .match = tiocx_match,
 147        .uevent = tiocx_uevent,
 148        .probe = cx_device_probe,
 149        .remove = cx_driver_remove,
 150};
 151
 152/**
 153 * cx_driver_register - Register the driver.
 154 * @cx_driver: driver table (cx_drv struct) from driver
 155 * 
 156 * Called from the driver init routine to register a driver.
 157 * The cx_drv struct contains the driver name, a pointer to
 158 * a table of part/mfg numbers and a pointer to the driver's
 159 * probe/attach routine.
 160 */
 161int 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
 169/**
 170 * cx_driver_unregister - Unregister the driver.
 171 * @cx_driver: driver table (cx_drv struct) from driver
 172 */
 173int cx_driver_unregister(struct cx_drv *cx_driver)
 174{
 175        driver_unregister(&cx_driver->driver);
 176        return 0;
 177}
 178
 179/**
 180 * cx_device_register - Register a device.
 181 * @nasid: device's nasid
 182 * @part_num: device's part number
 183 * @mfg_num: device's manufacturer number
 184 * @hubdev: hub info associated with this device
 185 * @bt: board type of the device
 186 *
 187 */
 188int
 189cx_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
 217        device_create_file(&cx_dev->dev, &dev_attr_cxdev_control);
 218
 219        return 0;
 220}
 221
 222/**
 223 * cx_device_unregister - Unregister a device.
 224 * @cx_dev: part/mfg id for the device
 225 */
 226int 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
 233/**
 234 * cx_device_reload - Reload the device.
 235 * @nasid: device's nasid
 236 * @part_num: device's part number
 237 * @mfg_num: device's manufacturer number
 238 *
 239 * Remove the device associated with 'nasid' from device list and then
 240 * call device-register with the given part/mfg numbers.
 241 */
 242static 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
 250static inline u64 tiocx_intr_alloc(nasid_t nasid, int widget,
 251                                        u64 sn_irq_info,
 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
 259        ia64_sal_oemcall_nolock(&rv, SN_SAL_IOIF_INTERRUPT,
 260                                SAL_INTR_ALLOC, nasid,
 261                                widget, sn_irq_info, req_irq,
 262                                req_nasid, req_slice);
 263        return rv.status;
 264}
 265
 266static 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
 273        ia64_sal_oemcall_nolock(&rv, SN_SAL_IOIF_INTERRUPT,
 274                                SAL_INTR_FREE, nasid,
 275                                widget, sn_irq_info->irq_irq,
 276                                sn_irq_info->irq_cookie, 0, 0);
 277}
 278
 279struct 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
 303void tiocx_irq_free(struct sn_irq_info *sn_irq_info)
 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
 316u64 tiocx_dma_addr(u64 addr)
 317{
 318        return PHYS_TO_TIODMA(addr);
 319}
 320
 321u64 tiocx_swin_base(int nasid)
 322{
 323        return TIO_SWIN_BASE(nasid, TIOCX_CORELET);
 324}
 325
 326EXPORT_SYMBOL(cx_driver_register);
 327EXPORT_SYMBOL(cx_driver_unregister);
 328EXPORT_SYMBOL(cx_device_register);
 329EXPORT_SYMBOL(cx_device_unregister);
 330EXPORT_SYMBOL(tiocx_irq_alloc);
 331EXPORT_SYMBOL(tiocx_irq_free);
 332EXPORT_SYMBOL(tiocx_bus_type);
 333EXPORT_SYMBOL(tiocx_dma_addr);
 334EXPORT_SYMBOL(tiocx_swin_base);
 335
 336static 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
 361static 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
 372static 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:
 387        case L1_BOARDTYPE_DAYTONA:
 388                *bt = ioboard_type;
 389                return 1;
 390        }
 391
 392        return 0;
 393}
 394
 395static 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
 406static 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
 445static 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
 455static 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
 487DEVICE_ATTR(cxdev_control, 0644, show_cxdev_control, store_cxdev_control);
 488
 489static int __init tiocx_init(void)
 490{
 491        cnodeid_t cnodeid;
 492        int found_tiocx_device = 0;
 493        int err;
 494
 495        if (!ia64_platform_is("sn2"))
 496                return 0;
 497
 498        err = bus_register(&tiocx_bus_type);
 499        if (err)
 500                return err;
 501
 502        for (cnodeid = 0; cnodeid < num_cnodes; cnodeid++) {
 503                nasid_t nasid;
 504                int bt;
 505
 506                nasid = cnodeid_to_nasid(cnodeid);
 507
 508                if ((nasid & 0x1) && is_fpga_tio(nasid, &bt)) {
 509                        struct hubdev_info *hubdev;
 510                        struct xwidget_info *widgetp;
 511
 512                        DBG("Found TIO at nasid 0x%x\n", nasid);
 513
 514                        hubdev =
 515                            (struct hubdev_info *)(NODEPDA(cnodeid)->pdinfo);
 516
 517                        widgetp = &hubdev->hdi_xwidget_info[TIOCX_CORELET];
 518
 519                        /* The CE hangs off of the CX port but is not an FPGA */
 520                        if (widgetp->xwi_hwid.part_num == TIO_CE_ASIC_PARTNUM)
 521                                continue;
 522
 523                        tio_corelet_reset(nasid, TIOCX_CORELET);
 524                        tio_conveyor_enable(nasid);
 525
 526                        if (cx_device_register
 527                            (nasid, widgetp->xwi_hwid.part_num,
 528                             widgetp->xwi_hwid.mfg_num, hubdev, bt) < 0)
 529                                return -ENXIO;
 530                        else
 531                                found_tiocx_device++;
 532                }
 533        }
 534
 535        /* It's ok if we find zero devices. */
 536        DBG("found_tiocx_device= %d\n", found_tiocx_device);
 537
 538        return 0;
 539}
 540
 541static int cx_remove_device(struct device * dev, void * data)
 542{
 543        struct cx_dev *cx_dev = to_cx_dev(dev);
 544        device_remove_file(dev, &dev_attr_cxdev_control);
 545        cx_device_unregister(cx_dev);
 546        return 0;
 547}
 548
 549static void __exit tiocx_exit(void)
 550{
 551        DBG("tiocx_exit\n");
 552
 553        /*
 554         * Unregister devices.
 555         */
 556        bus_for_each_dev(&tiocx_bus_type, NULL, NULL, cx_remove_device);
 557        bus_unregister(&tiocx_bus_type);
 558}
 559
 560fs_initcall(tiocx_init);
 561module_exit(tiocx_exit);
 562
 563/************************************************************************
 564 * Module licensing and description
 565 ************************************************************************/
 566MODULE_LICENSE("GPL");
 567MODULE_AUTHOR("Bruce Losure <blosure@sgi.com>");
 568MODULE_DESCRIPTION("TIOCX module");
 569MODULE_SUPPORTED_DEVICE(DEVICE_NAME);
 570