linux/drivers/pci/hotplug/cpci_hotplug_core.c
<<
>>
Prefs
   1/*
   2 * CompactPCI Hot Plug Driver
   3 *
   4 * Copyright (C) 2002,2005 SOMA Networks, Inc.
   5 * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
   6 * Copyright (C) 2001 IBM Corp.
   7 *
   8 * All rights reserved.
   9 *
  10 * This program is free software; you can redistribute it and/or modify
  11 * it under the terms of the GNU General Public License as published by
  12 * the Free Software Foundation; either version 2 of the License, or (at
  13 * your option) any later version.
  14 *
  15 * This program is distributed in the hope that it will be useful, but
  16 * WITHOUT ANY WARRANTY; without even the implied warranty of
  17 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
  18 * NON INFRINGEMENT.  See the GNU General Public License for more
  19 * details.
  20 *
  21 * You should have received a copy of the GNU General Public License
  22 * along with this program; if not, write to the Free Software
  23 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  24 *
  25 * Send feedback to <scottm@somanetworks.com>
  26 */
  27
  28#include <linux/module.h>
  29#include <linux/kernel.h>
  30#include <linux/slab.h>
  31#include <linux/pci.h>
  32#include <linux/pci_hotplug.h>
  33#include <linux/init.h>
  34#include <linux/interrupt.h>
  35#include <asm/atomic.h>
  36#include <linux/delay.h>
  37#include <linux/kthread.h>
  38#include "cpci_hotplug.h"
  39
  40#define DRIVER_AUTHOR   "Scott Murray <scottm@somanetworks.com>"
  41#define DRIVER_DESC     "CompactPCI Hot Plug Core"
  42
  43#define MY_NAME "cpci_hotplug"
  44
  45#define dbg(format, arg...)                                     \
  46        do {                                                    \
  47                if (cpci_debug)                                 \
  48                        printk (KERN_DEBUG "%s: " format "\n",  \
  49                                MY_NAME , ## arg);              \
  50        } while (0)
  51#define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME , ## arg)
  52#define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME , ## arg)
  53#define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME , ## arg)
  54
  55/* local variables */
  56static DECLARE_RWSEM(list_rwsem);
  57static LIST_HEAD(slot_list);
  58static int slots;
  59static atomic_t extracting;
  60int cpci_debug;
  61static struct cpci_hp_controller *controller;
  62static struct task_struct *cpci_thread;
  63static int thread_finished;
  64
  65static int enable_slot(struct hotplug_slot *slot);
  66static int disable_slot(struct hotplug_slot *slot);
  67static int set_attention_status(struct hotplug_slot *slot, u8 value);
  68static int get_power_status(struct hotplug_slot *slot, u8 * value);
  69static int get_attention_status(struct hotplug_slot *slot, u8 * value);
  70static int get_adapter_status(struct hotplug_slot *slot, u8 * value);
  71static int get_latch_status(struct hotplug_slot *slot, u8 * value);
  72
  73static struct hotplug_slot_ops cpci_hotplug_slot_ops = {
  74        .enable_slot = enable_slot,
  75        .disable_slot = disable_slot,
  76        .set_attention_status = set_attention_status,
  77        .get_power_status = get_power_status,
  78        .get_attention_status = get_attention_status,
  79        .get_adapter_status = get_adapter_status,
  80        .get_latch_status = get_latch_status,
  81};
  82
  83static int
  84update_latch_status(struct hotplug_slot *hotplug_slot, u8 value)
  85{
  86        struct hotplug_slot_info info;
  87
  88        memcpy(&info, hotplug_slot->info, sizeof(struct hotplug_slot_info));
  89        info.latch_status = value;
  90        return pci_hp_change_slot_info(hotplug_slot, &info);
  91}
  92
  93static int
  94update_adapter_status(struct hotplug_slot *hotplug_slot, u8 value)
  95{
  96        struct hotplug_slot_info info;
  97
  98        memcpy(&info, hotplug_slot->info, sizeof(struct hotplug_slot_info));
  99        info.adapter_status = value;
 100        return pci_hp_change_slot_info(hotplug_slot, &info);
 101}
 102
 103static int
 104enable_slot(struct hotplug_slot *hotplug_slot)
 105{
 106        struct slot *slot = hotplug_slot->private;
 107        int retval = 0;
 108
 109        dbg("%s - physical_slot = %s", __func__, slot_name(slot));
 110
 111        if (controller->ops->set_power)
 112                retval = controller->ops->set_power(slot, 1);
 113        return retval;
 114}
 115
 116static int
 117disable_slot(struct hotplug_slot *hotplug_slot)
 118{
 119        struct slot *slot = hotplug_slot->private;
 120        int retval = 0;
 121
 122        dbg("%s - physical_slot = %s", __func__, slot_name(slot));
 123
 124        down_write(&list_rwsem);
 125
 126        /* Unconfigure device */
 127        dbg("%s - unconfiguring slot %s", __func__, slot_name(slot));
 128        if ((retval = cpci_unconfigure_slot(slot))) {
 129                err("%s - could not unconfigure slot %s",
 130                    __func__, slot_name(slot));
 131                goto disable_error;
 132        }
 133        dbg("%s - finished unconfiguring slot %s", __func__, slot_name(slot));
 134
 135        /* Clear EXT (by setting it) */
 136        if (cpci_clear_ext(slot)) {
 137                err("%s - could not clear EXT for slot %s",
 138                    __func__, slot_name(slot));
 139                retval = -ENODEV;
 140                goto disable_error;
 141        }
 142        cpci_led_on(slot);
 143
 144        if (controller->ops->set_power)
 145                if ((retval = controller->ops->set_power(slot, 0)))
 146                        goto disable_error;
 147
 148        if (update_adapter_status(slot->hotplug_slot, 0))
 149                warn("failure to update adapter file");
 150
 151        if (slot->extracting) {
 152                slot->extracting = 0;
 153                atomic_dec(&extracting);
 154        }
 155disable_error:
 156        up_write(&list_rwsem);
 157        return retval;
 158}
 159
 160static u8
 161cpci_get_power_status(struct slot *slot)
 162{
 163        u8 power = 1;
 164
 165        if (controller->ops->get_power)
 166                power = controller->ops->get_power(slot);
 167        return power;
 168}
 169
 170static int
 171get_power_status(struct hotplug_slot *hotplug_slot, u8 * value)
 172{
 173        struct slot *slot = hotplug_slot->private;
 174
 175        *value = cpci_get_power_status(slot);
 176        return 0;
 177}
 178
 179static int
 180get_attention_status(struct hotplug_slot *hotplug_slot, u8 * value)
 181{
 182        struct slot *slot = hotplug_slot->private;
 183
 184        *value = cpci_get_attention_status(slot);
 185        return 0;
 186}
 187
 188static int
 189set_attention_status(struct hotplug_slot *hotplug_slot, u8 status)
 190{
 191        return cpci_set_attention_status(hotplug_slot->private, status);
 192}
 193
 194static int
 195get_adapter_status(struct hotplug_slot *hotplug_slot, u8 * value)
 196{
 197        *value = hotplug_slot->info->adapter_status;
 198        return 0;
 199}
 200
 201static int
 202get_latch_status(struct hotplug_slot *hotplug_slot, u8 * value)
 203{
 204        *value = hotplug_slot->info->latch_status;
 205        return 0;
 206}
 207
 208static void release_slot(struct hotplug_slot *hotplug_slot)
 209{
 210        struct slot *slot = hotplug_slot->private;
 211
 212        kfree(slot->hotplug_slot->info);
 213        kfree(slot->hotplug_slot);
 214        if (slot->dev)
 215                pci_dev_put(slot->dev);
 216        kfree(slot);
 217}
 218
 219#define SLOT_NAME_SIZE  6
 220
 221int
 222cpci_hp_register_bus(struct pci_bus *bus, u8 first, u8 last)
 223{
 224        struct slot *slot;
 225        struct hotplug_slot *hotplug_slot;
 226        struct hotplug_slot_info *info;
 227        char name[SLOT_NAME_SIZE];
 228        int status = -ENOMEM;
 229        int i;
 230
 231        if (!(controller && bus))
 232                return -ENODEV;
 233
 234        /*
 235         * Create a structure for each slot, and register that slot
 236         * with the pci_hotplug subsystem.
 237         */
 238        for (i = first; i <= last; ++i) {
 239                slot = kzalloc(sizeof (struct slot), GFP_KERNEL);
 240                if (!slot)
 241                        goto error;
 242
 243                hotplug_slot =
 244                        kzalloc(sizeof (struct hotplug_slot), GFP_KERNEL);
 245                if (!hotplug_slot)
 246                        goto error_slot;
 247                slot->hotplug_slot = hotplug_slot;
 248
 249                info = kzalloc(sizeof (struct hotplug_slot_info), GFP_KERNEL);
 250                if (!info)
 251                        goto error_hpslot;
 252                hotplug_slot->info = info;
 253
 254                slot->bus = bus;
 255                slot->number = i;
 256                slot->devfn = PCI_DEVFN(i, 0);
 257
 258                snprintf(name, SLOT_NAME_SIZE, "%02x:%02x", bus->number, i);
 259
 260                hotplug_slot->private = slot;
 261                hotplug_slot->release = &release_slot;
 262                hotplug_slot->ops = &cpci_hotplug_slot_ops;
 263
 264                /*
 265                 * Initialize the slot info structure with some known
 266                 * good values.
 267                 */
 268                dbg("initializing slot %s", name);
 269                info->power_status = cpci_get_power_status(slot);
 270                info->attention_status = cpci_get_attention_status(slot);
 271
 272                dbg("registering slot %s", name);
 273                status = pci_hp_register(slot->hotplug_slot, bus, i, name);
 274                if (status) {
 275                        err("pci_hp_register failed with error %d", status);
 276                        goto error_info;
 277                }
 278                dbg("slot registered with name: %s", slot_name(slot));
 279
 280                /* Add slot to our internal list */
 281                down_write(&list_rwsem);
 282                list_add(&slot->slot_list, &slot_list);
 283                slots++;
 284                up_write(&list_rwsem);
 285        }
 286        return 0;
 287error_info:
 288        kfree(info);
 289error_hpslot:
 290        kfree(hotplug_slot);
 291error_slot:
 292        kfree(slot);
 293error:
 294        return status;
 295}
 296
 297int
 298cpci_hp_unregister_bus(struct pci_bus *bus)
 299{
 300        struct slot *slot;
 301        struct slot *tmp;
 302        int status = 0;
 303
 304        down_write(&list_rwsem);
 305        if (!slots) {
 306                up_write(&list_rwsem);
 307                return -1;
 308        }
 309        list_for_each_entry_safe(slot, tmp, &slot_list, slot_list) {
 310                if (slot->bus == bus) {
 311                        list_del(&slot->slot_list);
 312                        slots--;
 313
 314                        dbg("deregistering slot %s", slot_name(slot));
 315                        status = pci_hp_deregister(slot->hotplug_slot);
 316                        if (status) {
 317                                err("pci_hp_deregister failed with error %d",
 318                                    status);
 319                                break;
 320                        }
 321                }
 322        }
 323        up_write(&list_rwsem);
 324        return status;
 325}
 326
 327/* This is the interrupt mode interrupt handler */
 328static irqreturn_t
 329cpci_hp_intr(int irq, void *data)
 330{
 331        dbg("entered cpci_hp_intr");
 332
 333        /* Check to see if it was our interrupt */
 334        if ((controller->irq_flags & IRQF_SHARED) &&
 335            !controller->ops->check_irq(controller->dev_id)) {
 336                dbg("exited cpci_hp_intr, not our interrupt");
 337                return IRQ_NONE;
 338        }
 339
 340        /* Disable ENUM interrupt */
 341        controller->ops->disable_irq();
 342
 343        /* Trigger processing by the event thread */
 344        wake_up_process(cpci_thread);
 345        return IRQ_HANDLED;
 346}
 347
 348/*
 349 * According to PICMG 2.1 R2.0, section 6.3.2, upon
 350 * initialization, the system driver shall clear the
 351 * INS bits of the cold-inserted devices.
 352 */
 353static int
 354init_slots(int clear_ins)
 355{
 356        struct slot *slot;
 357        struct pci_dev* dev;
 358
 359        dbg("%s - enter", __func__);
 360        down_read(&list_rwsem);
 361        if (!slots) {
 362                up_read(&list_rwsem);
 363                return -1;
 364        }
 365        list_for_each_entry(slot, &slot_list, slot_list) {
 366                dbg("%s - looking at slot %s", __func__, slot_name(slot));
 367                if (clear_ins && cpci_check_and_clear_ins(slot))
 368                        dbg("%s - cleared INS for slot %s",
 369                            __func__, slot_name(slot));
 370                dev = pci_get_slot(slot->bus, PCI_DEVFN(slot->number, 0));
 371                if (dev) {
 372                        if (update_adapter_status(slot->hotplug_slot, 1))
 373                                warn("failure to update adapter file");
 374                        if (update_latch_status(slot->hotplug_slot, 1))
 375                                warn("failure to update latch file");
 376                        slot->dev = dev;
 377                }
 378        }
 379        up_read(&list_rwsem);
 380        dbg("%s - exit", __func__);
 381        return 0;
 382}
 383
 384static int
 385check_slots(void)
 386{
 387        struct slot *slot;
 388        int extracted;
 389        int inserted;
 390        u16 hs_csr;
 391
 392        down_read(&list_rwsem);
 393        if (!slots) {
 394                up_read(&list_rwsem);
 395                err("no slots registered, shutting down");
 396                return -1;
 397        }
 398        extracted = inserted = 0;
 399        list_for_each_entry(slot, &slot_list, slot_list) {
 400                dbg("%s - looking at slot %s", __func__, slot_name(slot));
 401                if (cpci_check_and_clear_ins(slot)) {
 402                        /*
 403                         * Some broken hardware (e.g. PLX 9054AB) asserts
 404                         * ENUM# twice...
 405                         */
 406                        if (slot->dev) {
 407                                warn("slot %s already inserted",
 408                                     slot_name(slot));
 409                                inserted++;
 410                                continue;
 411                        }
 412
 413                        /* Process insertion */
 414                        dbg("%s - slot %s inserted", __func__, slot_name(slot));
 415
 416                        /* GSM, debug */
 417                        hs_csr = cpci_get_hs_csr(slot);
 418                        dbg("%s - slot %s HS_CSR (1) = %04x",
 419                            __func__, slot_name(slot), hs_csr);
 420
 421                        /* Configure device */
 422                        dbg("%s - configuring slot %s",
 423                            __func__, slot_name(slot));
 424                        if (cpci_configure_slot(slot)) {
 425                                err("%s - could not configure slot %s",
 426                                    __func__, slot_name(slot));
 427                                continue;
 428                        }
 429                        dbg("%s - finished configuring slot %s",
 430                            __func__, slot_name(slot));
 431
 432                        /* GSM, debug */
 433                        hs_csr = cpci_get_hs_csr(slot);
 434                        dbg("%s - slot %s HS_CSR (2) = %04x",
 435                            __func__, slot_name(slot), hs_csr);
 436
 437                        if (update_latch_status(slot->hotplug_slot, 1))
 438                                warn("failure to update latch file");
 439
 440                        if (update_adapter_status(slot->hotplug_slot, 1))
 441                                warn("failure to update adapter file");
 442
 443                        cpci_led_off(slot);
 444
 445                        /* GSM, debug */
 446                        hs_csr = cpci_get_hs_csr(slot);
 447                        dbg("%s - slot %s HS_CSR (3) = %04x",
 448                            __func__, slot_name(slot), hs_csr);
 449
 450                        inserted++;
 451                } else if (cpci_check_ext(slot)) {
 452                        /* Process extraction request */
 453                        dbg("%s - slot %s extracted",
 454                            __func__, slot_name(slot));
 455
 456                        /* GSM, debug */
 457                        hs_csr = cpci_get_hs_csr(slot);
 458                        dbg("%s - slot %s HS_CSR = %04x",
 459                            __func__, slot_name(slot), hs_csr);
 460
 461                        if (!slot->extracting) {
 462                                if (update_latch_status(slot->hotplug_slot, 0)) {
 463                                        warn("failure to update latch file");
 464                                }
 465                                slot->extracting = 1;
 466                                atomic_inc(&extracting);
 467                        }
 468                        extracted++;
 469                } else if (slot->extracting) {
 470                        hs_csr = cpci_get_hs_csr(slot);
 471                        if (hs_csr == 0xffff) {
 472                                /*
 473                                 * Hmmm, we're likely hosed at this point, should we
 474                                 * bother trying to tell the driver or not?
 475                                 */
 476                                err("card in slot %s was improperly removed",
 477                                    slot_name(slot));
 478                                if (update_adapter_status(slot->hotplug_slot, 0))
 479                                        warn("failure to update adapter file");
 480                                slot->extracting = 0;
 481                                atomic_dec(&extracting);
 482                        }
 483                }
 484        }
 485        up_read(&list_rwsem);
 486        dbg("inserted=%d, extracted=%d, extracting=%d",
 487            inserted, extracted, atomic_read(&extracting));
 488        if (inserted || extracted)
 489                return extracted;
 490        else if (!atomic_read(&extracting)) {
 491                err("cannot find ENUM# source, shutting down");
 492                return -1;
 493        }
 494        return 0;
 495}
 496
 497/* This is the interrupt mode worker thread body */
 498static int
 499event_thread(void *data)
 500{
 501        int rc;
 502
 503        dbg("%s - event thread started", __func__);
 504        while (1) {
 505                dbg("event thread sleeping");
 506                set_current_state(TASK_INTERRUPTIBLE);
 507                schedule();
 508                if (kthread_should_stop())
 509                        break;
 510                do {
 511                        rc = check_slots();
 512                        if (rc > 0) {
 513                                /* Give userspace a chance to handle extraction */
 514                                msleep(500);
 515                        } else if (rc < 0) {
 516                                dbg("%s - error checking slots", __func__);
 517                                thread_finished = 1;
 518                                goto out;
 519                        }
 520                } while (atomic_read(&extracting) && !kthread_should_stop());
 521                if (kthread_should_stop())
 522                        break;
 523
 524                /* Re-enable ENUM# interrupt */
 525                dbg("%s - re-enabling irq", __func__);
 526                controller->ops->enable_irq();
 527        }
 528 out:
 529        return 0;
 530}
 531
 532/* This is the polling mode worker thread body */
 533static int
 534poll_thread(void *data)
 535{
 536        int rc;
 537
 538        while (1) {
 539                if (kthread_should_stop() || signal_pending(current))
 540                        break;
 541                if (controller->ops->query_enum()) {
 542                        do {
 543                                rc = check_slots();
 544                                if (rc > 0) {
 545                                        /* Give userspace a chance to handle extraction */
 546                                        msleep(500);
 547                                } else if (rc < 0) {
 548                                        dbg("%s - error checking slots", __func__);
 549                                        thread_finished = 1;
 550                                        goto out;
 551                                }
 552                        } while (atomic_read(&extracting) && !kthread_should_stop());
 553                }
 554                msleep(100);
 555        }
 556 out:
 557        return 0;
 558}
 559
 560static int
 561cpci_start_thread(void)
 562{
 563        if (controller->irq)
 564                cpci_thread = kthread_run(event_thread, NULL, "cpci_hp_eventd");
 565        else
 566                cpci_thread = kthread_run(poll_thread, NULL, "cpci_hp_polld");
 567        if (IS_ERR(cpci_thread)) {
 568                err("Can't start up our thread");
 569                return PTR_ERR(cpci_thread);
 570        }
 571        thread_finished = 0;
 572        return 0;
 573}
 574
 575static void
 576cpci_stop_thread(void)
 577{
 578        kthread_stop(cpci_thread);
 579        thread_finished = 1;
 580}
 581
 582int
 583cpci_hp_register_controller(struct cpci_hp_controller *new_controller)
 584{
 585        int status = 0;
 586
 587        if (controller)
 588                return -1;
 589        if (!(new_controller && new_controller->ops))
 590                return -EINVAL;
 591        if (new_controller->irq) {
 592                if (!(new_controller->ops->enable_irq &&
 593                     new_controller->ops->disable_irq))
 594                        status = -EINVAL;
 595                if (request_irq(new_controller->irq,
 596                               cpci_hp_intr,
 597                               new_controller->irq_flags,
 598                               MY_NAME,
 599                               new_controller->dev_id)) {
 600                        err("Can't get irq %d for the hotplug cPCI controller",
 601                            new_controller->irq);
 602                        status = -ENODEV;
 603                }
 604                dbg("%s - acquired controller irq %d",
 605                    __func__, new_controller->irq);
 606        }
 607        if (!status)
 608                controller = new_controller;
 609        return status;
 610}
 611
 612static void
 613cleanup_slots(void)
 614{
 615        struct slot *slot;
 616        struct slot *tmp;
 617
 618        /*
 619         * Unregister all of our slots with the pci_hotplug subsystem,
 620         * and free up all memory that we had allocated.
 621         */
 622        down_write(&list_rwsem);
 623        if (!slots)
 624                goto cleanup_null;
 625        list_for_each_entry_safe(slot, tmp, &slot_list, slot_list) {
 626                list_del(&slot->slot_list);
 627                pci_hp_deregister(slot->hotplug_slot);
 628        }
 629cleanup_null:
 630        up_write(&list_rwsem);
 631        return;
 632}
 633
 634int
 635cpci_hp_unregister_controller(struct cpci_hp_controller *old_controller)
 636{
 637        int status = 0;
 638
 639        if (controller) {
 640                if (!thread_finished)
 641                        cpci_stop_thread();
 642                if (controller->irq)
 643                        free_irq(controller->irq, controller->dev_id);
 644                controller = NULL;
 645                cleanup_slots();
 646        } else
 647                status = -ENODEV;
 648        return status;
 649}
 650
 651int
 652cpci_hp_start(void)
 653{
 654        static int first = 1;
 655        int status;
 656
 657        dbg("%s - enter", __func__);
 658        if (!controller)
 659                return -ENODEV;
 660
 661        down_read(&list_rwsem);
 662        if (list_empty(&slot_list)) {
 663                up_read(&list_rwsem);
 664                return -ENODEV;
 665        }
 666        up_read(&list_rwsem);
 667
 668        status = init_slots(first);
 669        if (first)
 670                first = 0;
 671        if (status)
 672                return status;
 673
 674        status = cpci_start_thread();
 675        if (status)
 676                return status;
 677        dbg("%s - thread started", __func__);
 678
 679        if (controller->irq) {
 680                /* Start enum interrupt processing */
 681                dbg("%s - enabling irq", __func__);
 682                controller->ops->enable_irq();
 683        }
 684        dbg("%s - exit", __func__);
 685        return 0;
 686}
 687
 688int
 689cpci_hp_stop(void)
 690{
 691        if (!controller)
 692                return -ENODEV;
 693        if (controller->irq) {
 694                /* Stop enum interrupt processing */
 695                dbg("%s - disabling irq", __func__);
 696                controller->ops->disable_irq();
 697        }
 698        cpci_stop_thread();
 699        return 0;
 700}
 701
 702int __init
 703cpci_hotplug_init(int debug)
 704{
 705        cpci_debug = debug;
 706        return 0;
 707}
 708
 709void __exit
 710cpci_hotplug_exit(void)
 711{
 712        /*
 713         * Clean everything up.
 714         */
 715        cpci_hp_stop();
 716        cpci_hp_unregister_controller(controller);
 717}
 718
 719EXPORT_SYMBOL_GPL(cpci_hp_register_controller);
 720EXPORT_SYMBOL_GPL(cpci_hp_unregister_controller);
 721EXPORT_SYMBOL_GPL(cpci_hp_register_bus);
 722EXPORT_SYMBOL_GPL(cpci_hp_unregister_bus);
 723EXPORT_SYMBOL_GPL(cpci_hp_start);
 724EXPORT_SYMBOL_GPL(cpci_hp_stop);
 725