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